C# 开关类型

C# switch with types

编辑:


我有以下代码检查给定的 PropertyInfotype

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)) {
    // ...
}

有没有办法在这种情况下使用 switch 语句?这是我目前的解决方案:

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

我认为这不是最佳解决方案,因为现在我必须给出给定 type 的完全限定 String 值。有什么建议吗?

没有通用的方法,但更常见的是,那些分支包含非常相似的代码。几乎总是对我有用的一种模式是使用字典;

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'
}

我就如实回答:没办法。

从 C# 6 开始,

switch 仅支持精确匹配某些类型的常量。您不是要匹配常量。您多次调用 IsAssignableFrom 方法。

请注意,IsAssignableFrom 与完全匹配的类型相同。因此,任何基于相等比较或哈希表的解决方案都行不通。

我认为您的 if ... else if 解决方案完全没问题。

使用您拥有的 ToString 方法,而不是文字值使用 case typeof(string).Name(如果可能的话)现在我面前没有 vs。

首先,在继承类型的情况下,IsAssignableFrom 比仅进行字符串比较要好。 例如 typeof(TextReader).IsAssignableFrom(typeof(StreamReader)) 将是 true,因为 StreamReader 继承自 TextReader,也适用于接口。

如果只需要直接比较,我可以建议创建Dictionary<Type,Action<PropertyInfo>>,例如:

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

那么你可以这样使用它:

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

当然,在这种情况下,您必须为每种类型创建方法,或者在创建字典期间编写代码。

现在在 C# 7.0 中可用。

这个解决方案是针对我原来的问题; switch 语句适用于 PropertyInfovalue 而不是其 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;
}

稍微通用一点的答案:

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/

如 Mårten Wikström 的回答所示:“How to use switch-case on a Type?”您可以这样使用 Type.GetTypeCode

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...
}