4

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
  • 3,584
  • 4
  • 23
  • 53
  • 1
    I think that you should look at this[C# - Is there a better alternative than this to 'switch on type'?](http://stackoverflow.com/questions/298976/c-sharp-is-there-a-better-alternative-than-this-to-switch-on-type). Possible duplicate problem if not question. – ChrisBD Jan 25 '12 at 15:07
  • I think that using the `is` keyword is in most cases preferable to comparing the class name, no matter if you use `switch` or a lot of `if`s as proposed. – Erich Kitzmueller Jan 25 '12 at 15:07
  • @ChrisBD: thanks for this link. That's looks pretty great. I'm looking forward to implement this. – Michael Schnerring Jan 25 '12 at 15:40
  • possible duplicate of [Switch over PropertyType](http://stackoverflow.com/questions/91563/switch-over-propertytype) – outis Jan 26 '12 at 04:55

5 Answers5

12

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.

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
7

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.

jason
  • 236,483
  • 35
  • 423
  • 525
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));
}
Michael Schnerring
  • 3,584
  • 4
  • 23
  • 53
1

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;
        }
ERukes
  • 51
  • 6
0

Use function overloading, and maybe the dynamic keyword. Or use the Visitor pattern. Either way gives you dispatch based on the runtime type of a variable.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 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. – Michael Schnerring Jan 25 '12 at 15:13
  • @ebeeb: Well, your question didn't say any of that. And you still haven't given any specific requirements which would exclude this approach. – Ben Voigt Jan 25 '12 at 16:11
  • True, my question didn't. I didn't mean that overloading is the wrong approach in general. – Michael Schnerring Jan 25 '12 at 16:30
  • @ebeeb: Mind explaining how "a ridiculous number of overloads" is any worse than "a ridiculous number of switch cases"? – Ben Voigt Jan 25 '12 at 16:36
  • Our current solution were overloads. It turned out that through that switch statement lots of code repetition was avoided. – Michael Schnerring Jan 25 '12 at 16:45
  • @ebeeb: Overloads can actually call each other (much more flexible than `goto case`), reuse should be better between functions than between case statements. – Ben Voigt Jan 25 '12 at 16:48
  • 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. – Michael Schnerring Jan 25 '12 at 16:55
  • @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. – Ben Voigt Jan 25 '12 at 16:57