C#泛型和类型检查

Jon*_*Jon 72 c# generics types

我有一个使用a IList<T>作为参数的方法.我需要检查该T对象的类型是什么,并根据它做一些事情.我试图使用该T值,但编译器不允许它.我的解决方案如下:

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        if (clause[0] is int || clause[0] is decimal)
        {
           //do something
        }
        else if (clause[0] is String)
        {
           //do something else
        }
        else if (...) //etc for all the types
        else
        {
           throw new ApplicationException("Invalid type");
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

必须有更好的方法来做到这一点.有什么方法可以检查T传入的类型然后使用switch语句?

jon*_*nii 104

你可以使用重载:

public static string BuildClause(List<string> l){...}

public static string BuildClause(List<int> l){...}

public static string BuildClause<T>(List<T> l){...}
Run Code Online (Sandbox Code Playgroud)

或者您可以检查泛型参数的类型:

Type listType = typeof(T);
if(listType == typeof(int)){...}
Run Code Online (Sandbox Code Playgroud)

  • +1:在设计和长期可维护性方面,重载绝对是*最佳*解决方案.通用参数的运行时类型检查对于直接编码来说似乎太讽刺了. (21认同)
  • 你不应该依赖泛型重载(参见[这个答案](http://stackoverflow.com/a/601301/1631910))如果你的重载功能不同(也考虑副作用),因为你无法保证将调用更专业的重载.这里的规则是这样的:如果你必须为特定类型做专门的逻辑,那么你必须检查该类型而不是使用重载; 但是,如果您只是PREFER做专门的逻辑(即性能改进),但所有重载包括一般情况导致相同的结果,那么您可以使用重载而不是类型检查. (6认同)

bdo*_*den 19

你可以用typeof(T).

private static string BuildClause<T>(IList<T> clause)
{
     Type itemType = typeof(T);
     if(itemType == typeof(int) || itemType == typeof(decimal))
    ...
}
Run Code Online (Sandbox Code Playgroud)


Jai*_*der 11

我希望你觉得这有帮助:

  • typeof(IList<T>).IsGenericType == true
  • typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
  • typeof(IList<int>).GetGenericArguments()[0] == typeof(int)

https://dotnetfiddle.net/5qUZnt


Kit*_*Kit 7

而且,由于 C# 已经发展,您(现在)可以使用模式匹配

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        switch (clause[0])
        {
            case int x: // do something with x, which is an int here...
            case decimal x: // do something with x, which is a decimal here...
            case string x: // do something with x, which is a string here...
            ...
            default: throw new ApplicationException("Invalid type");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

再次使用C# 8.0 中的switch 表达式,语法变得更加简洁。

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        return clause[0] switch
        {
            int x => "some string related to this int",
            decimal x => "some string related to this decimal",
            string x => x,
            ...,
            _ => throw new ApplicationException("Invalid type")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Jar*_*Par 6

默认情况下,知道没有一个好方法.一段时间后,我对此感到沮丧,写了一个小实用程序类,帮助了一点,使语法更清晰.基本上它将代码转换为

TypeSwitcher.Do(clause[0],
  TypeSwitch.Case<int>(x => ...),  // x is an int
  TypeSwitch.Case<decimal>(d => ...), // d is a decimal 
  TypeSwitch.Case<string>(s => ...)); // s is a string
Run Code Online (Sandbox Code Playgroud)

完整的博客文章和有关实施的详细信息可在此处获得