【问题标题】:Checking type parameter of a generic method in C#在 C# 中检查泛型方法的类型参数
【发布时间】:2010-01-05 06:21:50
【问题描述】:

是否可以在 C# 中做这样的事情:

public void DoSomething<T>(T t)  
{
    if (T is MyClass)
    {
        MyClass mc = (MyClass)t 
        ...
    }
    else if (T is List<MyClass>)
    {
        List<MyClass> lmc = (List<MyClass>)t
        ...
    }
}

【问题讨论】:

    标签: c# generics typechecking


    【解决方案1】:

    是的:

    if (typeof(T) == typeof(MyClass))
    {
        MyClass mc = (MyClass)(object) t;
    }
    else if (typeof(T) == typeof(List<MyClass>))
    {
        List<MyClass> lmc = (List<MyClass>)(object) t;
    }
    

    您需要通过强制转换为对象,这有点奇怪,但这正是泛型的工作方式 - 泛型类型的转换没有您预期的那么多。

    当然另一种选择是使用正常的执行时间检查:

    MyClass mc = t as MyClass;
    if (mc != null)
    {
        // ...
    }
    else
    {
        List<MyClass> lmc = t as List<MyClass>;
        if (lmc != null)
        {
            // ...
        }
    }
    

    当然,如果t 为空,那将与第一个代码块的行为不同。

    我会尝试在可能的情况下避免这种代码,但是 - 有时可能是必要的,但泛型方法的想法是能够编写 generic对任何类型都以相同方式工作的代码。

    【讨论】:

    • 我实际上有一个更复杂的问题。如果 MyClass 派生自 MyBaseClass 并且还有更多 MyClass 都派生自 MyBaseClass 怎么办?
    • @synergetic:您已经描述了类型层次结构,但没有描述您想用它做什么。您可以使用反射(例如 typeof(T).BaseTypetypeof(T).IsAssignableFrom(...) 来探索类型层次结构,如果这有用的话。但如果可能的话,我仍然会尽量避免它:)
    • 讨厌在这里成为一条湿毛巾,但在 T 实际上是盒装类型的情况下(例如:object boxedMyClass = new MyClass()),这个答案就不足了。在这种情况下,typeof 运算符返回的类型将是 object,而不是 MyClass,导致上述 typeof 检查失败。在我看来,这就是 C# 作为一门语言的不足之处:处理泛型类型审查和复杂的本体逻辑(例如,没有办法在 C# 中说“aLifeform 是哺乳动物而不是熊”之类的话,而无需进行反思)。
    • @rmiesen:不清楚你的意思。请注意,classes 并没有首先涉及拳击。 T 将是您指定的任何内容,但它是 compile-time 类型。如果您想要对象的 execution-time 类型,请致电GetType
    • @JonSkeet:我想避免这样的实现(God 方法,它能够消化任何类型),但如果没有运行时类型检查,我无法看到如何做到这一点。假设泛型方法的签名类似于:T Obfuscate&lt;T&gt;(T value) where T : IConvertible,因此它可以混淆值类型和字符串(假设枚举不是IConvertible 的一部分)。 如何实现这样的通用方法而不为每个单独传递的类型值实现它? stackoverflow.com/q/23957027/75642
    【解决方案2】:

    从 C# 7 开始,您可以使用 is 运算符以简洁的方式执行此操作:

    public void DoSomething<T>(T value)  
    {
        if (value is MyClass mc)
        {
            ...
        }
        else if (value is List<MyClass> lmc)
        {
            ...
        }
    }
    

    查看文档:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is#pattern-matching-with-is

    【讨论】:

      【解决方案3】:

      现在是 2017 年,我们现在有了带有模式匹配的 C# 7。如果你的类型 T 继承 object 你可以这样编码

      void Main()
      {
          DoSomething(new MyClass { a = 5 });
          DoSomething(new List<MyClass> { new MyClass { a = 5 }, new MyClass { a = 5 }});
      }
      
      
      public void DoSomething(object t)
      {
          switch (t)
          {
              case MyClass c:
                  Console.WriteLine($"class.a = {c.a}");
                  break;
              case List<MyClass> l:
                  Console.WriteLine($"list.count = {l.Count}");
                  break;
          }
      }
      
      class MyClass
      {
          public int a { get; set;}
      }
      

      【讨论】:

      • 请注意,在 C# 7.1 之前,您不能将其用于泛型。
      • 如果你把它装箱,你可以(无论如何,这是 C# 7.1 编译器在幕后所做的)。
      • 如果您有第二种类型具有相同的属性名称,假设是 MyClass2 并且它也有一个属性“a” - 有没有办法堆叠 MyClass 和 MyClass2 的两个 case 语句,以便不必重复相同的 Console.WriteLine() 语句?
      • 你的意思是case MyClass c: case MyClass2 d: 这是不可能的,根据文档:这些变量都没有在这个 switch 块中明确分配。如果这些情况中的任何一种匹配,则显然已分配了一个变量。但是,在编译时无法判断哪个已分配,因为任何一种情况都可能在运行时匹配。
      【解决方案4】:

      我认为您的设计有问题。 您想在已经通用的方法中比较类型。泛型旨在处理类型变量的情况。我建议这样做..

      //Generic Overload 1
      public void DoSomething<T>(T t)
          where T : MyClass
      {
          ...
      }
      
      //Generic Overload 2
      public void DoSomething<T>(T t)
          where T : List<MyClass>
      {
          ...
      }
      

      【讨论】:

      • 就此而言,您可以删除泛型并指定参数的类型。但是 +1 因为我想要没有参数的多态性。
      • 这究竟是如何工作的?正如这里指出的stackoverflow.com/questions/15367032/…“你不能通过泛型约束重载”。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      相关资源
      最近更新 更多