如何在C#中从派生类型获取泛型类型的通用参数类型。

6

假设我有:

class MyBase<T1, T2>{}

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

class MyConcrete1 : MyConcreteBase<ConcreteType2>{}

class MyConcrete2 : MyBase<ConcreteType1, ConcreteType2>{}

如果我有MyConcrete1MyConcrete2或者MyConcreteBase的实例,或者任何从MyBase<T1, T2>派生的类型的实例,我该如何获取T1T2的类型?目前我的做法是使用.GetType().BaseType沿着继承链向上查找,同时判断BaseType.Name.StartsWith("MyBase"),然后使用.GetGenericArguments()。虽然这种方法可以正常工作,但我并不满意,特别是.StartsWith("MyBase")这一部分。请问还有其他建议吗?

我的回答被一个快速的踩贴者给踩了。 - BoltClock
@BoltClock 这是对错误问题的错误答案。 - siride
@siride:确实。我只是在评论它的快速性 :) - BoltClock
6个回答

3
您可以沿着继承层次结构向上查找一个GenericType,它是从BaseType<,>构造的。尝试使用以下方法:
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<,>))(可能需要反转BaseTypeMyBase<,> -- 我总是搞混)。

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

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

我必须问一下,你为什么需要提取这些类型参数?在我的看法中,这是代码味道的表现。
编辑:我应该补充说明,您不能直接使用IsAssignableFrom来检查泛型。请查看此答案以获取完整解决方案:如何检测类型是否为另一个通用类型

@siride - 我需要它是因为我在我的MVC应用程序中有一个通用的ReportController,其中有一个名为Show(string reportName)的操作。然后我有ReportBase <TEntiy,TViewItem>,然后是WhateverReport <TViewItem>:ReportBase <WhateverEntity,TViewModel>,然后是YearlyWhateverReport:WhateverReport <WhateverYearlyViewItem> ...等等。所以我需要知道TEntity的类型,以将其传递给存储库并获取所需的IQueryable,以将其提供给ReportBase.Generate(IQueryable entityQuery)。 - Andrej Slivko
@qrow,我认为在基类中将类型公开为属性是最好的选择。这也比使用反射快得多。 - siride
@siride 我回来工作了,终于可以测试这里提出的解决方案。我意识到在 MyBase 中公开类型对我来说不太合适,因为我不知道派生对象的类型,也不能像 ((MyBase<,>)report).T1_Type 这样做...所以也许反射是我唯一的选择。 - Andrej Slivko
最终我使用了dynamic而不是object,并且能够调用T1_Type。因此,我坚持使用这个解决方案。 - Andrej Slivko
@qrow:你不需要转换为基类...所有派生类也将具有这些属性。动态可能不是最好的解决方案,因为它会产生开销并降低类型安全性。 - siride
显示剩余3条评论

0

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

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

0
这里有一个函数,它将接受任何类型(假设它具有带有通用参数的基类),并返回该实例的基类的通用参数的类型:
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();
    }

这种方法的好处在于您不需要了解基类或其后续派生类中的任何内容。


0

如果您能多告诉我们一些关于您想要实现的目标,那就太好了。猜测您的想法,我会说:只需遍历完整个继承链,直到 BaseType 不为 null 为止。然后检查每个类型是否为泛型,如果是,则调用 GetGenericArguments。这应该只需要几个条件语句和一个或两个 LINQ 表达式。


siride也问了这个的目的,你可以在那里查看我的解释。 - Andrej Slivko

-1

@siride - 我认为这仍然是回答“如果我有MyConcrete1或MyConcrete2或MyConcreteBase的实例或任何其他派生自MyBase<T1,T2>类型的实例,我如何获得T1和T2的类型”的问题。 - Simon Mourier
但他已经知道那部分了。他需要帮助的是算法的另一部分。这就好像有人问如何解析数学表达式,却在语法上遇到了困难,你建议他使用Regex类将事物分解为标记。从技术上讲是正确的,但对于手头的问题并不是很有帮助。 - siride

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接