【问题标题】:How to get generic argument types of generic type from derived type in c#如何从 C# 中的派生类型获取泛型类型的泛型参数类型
【发布时间】:2013-11-27 01:05:05
【问题描述】:

假设我有:

class MyBase<T1, T2>{}

class MyConcreteBase<T2> : MyBase<ConcreteType1, T2>{}

class MyConcrete1 : MyConcreteBase<ConcreteType2>{}

class MyConcrete2 : MyBase<ConcreteType1, ConcreteType2>{}

如果我有 MyConcrete1MyConcrete2MyConcreteBase 或派生自 MyBase&lt;T1, T2&gt; 的任何其他类型实例,我如何获得 T1T2 的类型

我现在的做法是通过继承链“起床”,使用.GetType().BaseTypeBaseType.Name.StartsWith("MyBase"),然后使用.GetGenericArguments()

它正在工作,但我对此并不满意,尤其是.StartsWith("MyBase") 部分。

还有其他建议吗?

【问题讨论】:

  • 我的回答很快被否决了。
  • @BoltClock 这是对错误问题的错误答案。
  • @siride:确实。我只是在评论它有多快:)

标签: c# reflection


【解决方案1】:

您可以沿着继承层次结构查找由BaseType&lt;,&gt; 构造的GenericType。试试:

var type = c.GetType();
Type baseType = type;
while(null != (baseType = baseType.BaseType)) 
{
    if (baseType.IsGenericType) 
    {
        var generic = baseType.GetGenericTypeDefinition();
        if (generic == typeof(MyBase<,>)) 
        {
            var genericTypes = baseType.GetGenericArguments();
            // genericTypes has the type argument used to construct baseType.
            break;
        }
    }
}   

【讨论】:

    【解决方案2】:

    是的,不要使用字符串解析。你可以简单地使用BaseType.IsAssignableFrom(typeof(MyBase&lt;,&gt;))(可能必须颠倒BaseTypeMyBase&lt;,&gt;——我总是很困惑)。

    在基类中将类型公开为属性也可能更容易,例如:

    public Type T1_Type { get { return typeof(T1); } }
    public Type T2_Type { get { return typeof(T2); } }
    

    我不得不问,为什么需要提取这些类型参数?这对我来说是一种代码味道。

    编辑:我应该补充一点,你不能像泛型一样使用IsAssignableFrom。查看此答案以获得完整的解决方案:How To Detect If Type is Another Generic Type

    【讨论】:

    • @siride - 我需要它,因为我的 MVC 应用程序中有通用的 ReportController 并且我有操作 Show(string reportName)。然后我有 ReportBase 然后是 WhateverReport : ReportBase 然后是 YearlyWhateverReport : WhateverReport... 等等。所以我需要知道 TEntity 的类型才能将其传递给存储库并获取针头 IQueryable 以将其提供给 ReportBase.Generate(IQueryable entityQuery)
    • @qrow 那么我认为将类型公开为基类中的属性是最好的选择。它也比使用反射快很多。
    • @siride 你可能是对的,这可能是最简单的解决方案,不知何故我对那些通用操作使用了太多的反射,所以我没有考虑只在基类上公开通用 arg 类型跨度>
    • @siride 我回来工作了,我终于可以测试这里建议的解决方案了。我意识到在 MyBase 中公开类型对我来说不合适,因为我不知道派生对象的类型,而且我不能做类似 ((MyBase)report).T1_Type ... 所以可能是我唯一的选择毕竟是反思
    • 最后我使用动态而不是对象并且能够调用 T1_Type。所以我坚持这个解决方案
    【解决方案3】:

    如果您能告诉我们更多关于您想要实现的目标,那就太好了。猜猜你的想法是什么,我想说:只要BaseType不是null,就走完整的继承链。然后检查每种类型是否是通用的,如果是,请致电GetGenericArguments。应该是几个谓词和一两个 LINQ 表达式的问题。

    【讨论】:

    • siride 也询问了这样做的目的,所以你可以在那里查看我的解释。
    【解决方案4】:

    我想不出什么聪明的办法,所以我可能会这样做:

    class MyBase<T1, T2> {
        protected Type GetT1() { return typeof(T1); }
        protected Type GetT2() { return typeof(T2); }
    }
    

    【讨论】:

      【解决方案5】:

      这是一个函数,它可以接受任何类型(假设它有一个带有泛型参数的基类)并返回该实例的基类泛型参数的类型:

      public Type[] BaseGenericTypes<T>(T instance)
          {
              Type instanceType = instance.GetType();
      
              Type objectType = new object().GetType();
      
              while (instanceType.BaseType != objectType)
              {
                  instanceType = instanceType.BaseType;
              }
              return instanceType.GetGenericArguments();
          }
      

      这个方法的好处是你不需要知道任何关于基类或其任何后续派生类的信息。

      【讨论】:

        【解决方案6】:

        你的意思是:Type.GetGenericArguments Method

        【讨论】:

        • @siride - 我认为这仍然是“如果我有 MyConcrete1 或 MyConcrete2 或 MyConcreteBase 的实例或派生自 MyBase"
        • 但他已经知道那部分了。这是他需要帮助的算法的另一部分。就好像有人问如何解析数学表达式并且遇到语法问题,而您建议他使用 Regex 类将事物分解为标记。技术上是正确的,但对手头的问题没有帮助。
        • @siride - 我不明白他在找什么
        猜你喜欢
        • 2011-10-09
        • 1970-01-01
        • 2014-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-24
        • 2012-09-08
        • 1970-01-01
        相关资源
        最近更新 更多