switch statement: "a constant value is expected"

31,601

Solution 1

With the new pattern matching feature of C# 7 I would solve it in the following manner.

Here a simple Field and Document class

public class Field
{
    public string Label { get; set; }
}

public class Field<T> : Field
{
    public T Value { get; set; }
}

public class Document
{
    public string Content { get; set; }
}

And here a FieldOperator class which does some arbitrary changes to a list of Fields

public static class FieldOperator
{
    public static void Operate(Field[] fields)
    {
        foreach (var field in fields)
        {
            field.Label = field.GetType().ToString();
            switch (field)
            {
                case Field<Document> docField:
                    docField.Value.Content = "Foo Bar";
                    break;
                case Field<int> intField:
                    intField.Value = 600842;
                    break;
                default:
                    field.Label = "Oops";
                    break;
            }
        }
    }
}

Testing for correctness of these "operations"

[Test]
public void OperationsAreCorrect()
{            
    var docField = new Field<Document> {Value = new Document {Content = "Hello World"}};
    var intField = new Field<int> {Value = 17};
    var dateField = new Field<DateTime>();
    FieldOperator.Operate(new Field[] {docField, intField, dateField});

    Assert.IsTrue(docField.Label == docField.GetType().ToString());
    Assert.IsTrue(intField.Label == intField.GetType().ToString());
    Assert.IsTrue(dateField.Label == "Oops");

    Assert.IsTrue(docField.Value.Content == "Foo Bar");
    Assert.IsTrue(intField.Value == 600842);
    Assert.IsTrue(dateField.Value == default(DateTime));
}

Solution 2

Just use an if:

Type type = myField.GetType();
if (type == MyDataField.GetType())
{
    …
}
else if (type.ToString() == "MyDataField")
{
    …
}
else
{
    …
}

You even don't need to compare type names, but the Type objects (references) directly.

Solution 3

I refer you to the specification §8.7.2 which states for the grammar of a switch-label:

switch-label:
    case constant-expression:
    default:

Simply put, the case labels must be constants at compile-time. Note that typeof(MyDataField).ToString() is not a compile-time constant (it might look constant to you, but it's not because it can not be fully evaluated at compile time). §7.19 of the specification spells out very clearly what a constant is

You need to recode this as an if/else if/else.

Solution 4

the case statement requires a constant value, so where you have

 case MyDataField.GetType().ToString():

you would need to change that to the specific string that you are looking for:

case "BR549":
     break;

if you are trying to determine the field type, you can do something like this:

Int16 bob = 5;
TypeCode objType = (TypeCode) Enum.Parse(typeof(TypeCode), bob.GetType().ToString());

        switch (objType)
        {
            case TypeCode.DateTime:
                txtResults.Text = "  - bob is a DateTime.";
                break;
            case TypeCode.Int16:
                txtResults.Text = " - bob is an int16.";
                break;
            default:
                txtResults.Text = " - bob is an unknown type.";
                break;
        }
Share:
31,601
Michael Schnerring
Author by

Michael Schnerring

Updated on May 09, 2020

Comments

  • Michael Schnerring
    Michael Schnerring almost 4 years

    Currently I'm fighting with that "magical strings" issue:

    public class MyDataField
    {
        // class definition
    }
    
    // exuecuted method
    public void SwitchMultipleDataFields()
    {
        var myField = new MyDataField();
        switch(myField.GetType().ToString())
        {
            // only case, which works
            case "MyDataField":
                // case operations
                break;
    
            // other option:
            case typeof(MyDataField).ToString():
                // case operations
                break;
    
            // other cases of other FieldTypes
        }
    }
    

    Now I get the error Message I've written in the title of my thread. I think the problem is that this string is not a constant while "non-compile-time". So the only possible way to ask switch this is via explicitly determining the value of that case string. My problem just is that I don't get an compile error in case I'd rename the MyDataField class. So 90% of these classes are generic anyway. These are handled in the default of the switch statement. Isn't there another way than explicitly determining the value of the case value?

    Please don't argue about the sense of this method. I've just written that to illustrate my problem in an easier way

  • Michael Schnerring
    Michael Schnerring over 12 years
    We're building this solution to clean up our code. Because we would need a ridiculously large amount of overloads. This struct is pretty complex and it's not a viable solution through overloading, sorry.
  • Ben Voigt
    Ben Voigt over 12 years
    This doesn't handle user-defined types at all, which seems to be a requirement of the question.
  • Ben Voigt
    Ben Voigt over 12 years
    @ebeeb: Well, your question didn't say any of that. And you still haven't given any specific requirements which would exclude this approach.
  • Michael Schnerring
    Michael Schnerring over 12 years
    True, my question didn't. I didn't mean that overloading is the wrong approach in general.
  • Ben Voigt
    Ben Voigt over 12 years
    @ebeeb: Mind explaining how "a ridiculous number of overloads" is any worse than "a ridiculous number of switch cases"?
  • Michael Schnerring
    Michael Schnerring over 12 years
    Our current solution were overloads. It turned out that through that switch statement lots of code repetition was avoided.
  • Ben Voigt
    Ben Voigt over 12 years
    @ebeeb: Overloads can actually call each other (much more flexible than goto case), reuse should be better between functions than between case statements.
  • Michael Schnerring
    Michael Schnerring over 12 years
    I don't neglect this. In general it is as you said. For our special issue, simple as it is, the switch statement just reduces the amount of code. I didn't tell you about that in my start post. But that's usual. Non-complex questions are easier to answer. And I just told you that your solution is not viable for our project. I didn't downvote your post, because your answer actually answered my question as well. +1 because other ppl seemed to downvote after I've explained my issue more detailed.
  • Ben Voigt
    Ben Voigt over 12 years
    @ebeeb: Understood. I'm just suggesting that you ask whether the code reduction that came with switch was because of switch, or just part of the same rewrite.