budi
budi

Reputation: 6541

C# switch with types

EDIT: This is now available in C# 7.0.


I have the following piece of code that checks a given PropertyInfo's type.

PropertyInfo prop;

// init prop, etc...

if (typeof(String).IsAssignableFrom(prop.PropertyType)) {
    // ...
}
else if (typeof(Int32).IsAssignableFrom(prop.PropertyType)) {
    // ...
}
else if (typeof(DateTime).IsAssignableFrom(prop.PropertyType)) {
    // ...
}

Is there a way to use a switch statement in this scenario? This is my current solution:

switch (prop.PropertyType.ToString()) {
    case "System.String":
        // ...
        break;
    case "System.Int32":
        // ...
        break;
    case "System.DateTime":
        // ...
        break;
    default:
        // ...
        break;
}

I don't think this is the best solution, because now I have to give the fully qualified String value of the given type. Any tips?

Upvotes: 8

Views: 6530

Answers (6)

Joezer
Joezer

Reputation: 665

As shown in Mårten Wikström's answer: "How to use switch-case on a Type?" you can use Type.GetTypeCode as such:

switch (Type.GetTypeCode(type))
{
    case TypeCode.Int32:
       // It's an int
    break;

    case TypeCode.String:
       // It's a string
    break;

    // Other type code cases here...

    default:
        // Fallback to using if-else statements...
        if (type == typeof(MyCoolType))
        {
           // ...
        }
        else if (type == typeof(MyOtherType))
        {
           // ...
    } // etc...
}

Upvotes: 0

budi
budi

Reputation: 6541

This is now available in C# 7.0.

This solution is for my original question; switch statements work on the value of the PropertyInfo and not its PropertyType:

PropertyInfo prop;

// init prop, etc...

var value = prop.GetValue(null);

switch (value)
{
    case string s:
        // ...
        break;
    case int i:
        // ...
        break;
    case DateTime d:
        // ...
        break;
    default:
        // ...
        break;
}

Slightly more generic answer:

switch(shape)
{
    case Circle c:
        WriteLine($"circle with radius {c.Radius}");
        break;
    case Rectangle s when (s.Length == s.Height):
        WriteLine($"{s.Length} x {s.Height} square");
        break;
    case Rectangle r:
        WriteLine($"{r.Length} x {r.Height} rectangle");
        break;
    default:
        WriteLine("<unknown shape>");
        break;
    case null:
        throw new ArgumentNullException(nameof(shape));
}

Reference: https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

Upvotes: 7

Andrey Tretyak
Andrey Tretyak

Reputation: 3221

First of all IsAssignableFrom is better then just string comparing in case of inherited types. For example typeof(TextReader).IsAssignableFrom(typeof(StreamReader)) will be true, because StreamReader inherited from TextReader, also work's for interfaces.

If you need only direct comparison, I can suggest create Dictionary<Type,Action<PropertyInfo>>, for example:

var typeSelector = new Dictionary<Type, Action<PropertyInfo>>()
{
    {typeof(int), IntAction }
    {typeof(string), StringAction }
    {typeof(DateTime), DateTimeAction }
};

Then you can use it like this:

Action<PropertyInfo> action;
if (typeSelector.TryGetValue(prop.PropertyType, out action))
    action(prop);
else 
    throw new InvalidDataException("Unsupported type");

Sure in this case you will have to create method for each type, or write code during creation of dictionary.

Upvotes: 1

Steve Harris
Steve Harris

Reputation: 5109

Use the ToString method you have, but instead of literal values use case typeof(string).Name (if that's possible) don't have vs in front of me right now.

Upvotes: 1

usr
usr

Reputation: 171178

I'll answer the question exactly as asked: There is no way.

switch as of C# 6 only supports matching constants of certain types exactly. You are not trying to match constants. You are invoking the IsAssignableFrom method many times.

Note, that IsAssignableFrom is not identical to matching types exactly. Therefore, any solution based on equality comparisons or hash tables can't work.

I think the if ... else if solution that you have is totally fine.

Upvotes: 5

Steve Cooper
Steve Cooper

Reputation: 21480

There's no general way, but more often that not, those branches contain very similar code. One pattern that almost always works for me is to use a dictionary;

var myIndex = new Dictionary<Type, string> {
    { typeof(string), "some text" },    
    { typeof(int), "a whole number" },    
    { typeof(decimal), "a fraction" },    
};

string description;
if (myIndex.TryGetValue(prop.PropertyType, out description)) {
    Console.WriteLine("This type is " + description);
} else {
    // 'default'
}

Upvotes: 3

Related Questions