【问题标题】:Is there a way to determine if a generic type is built from a specific generic type definition?有没有办法确定泛型类型是否是从特定的泛型类型定义构建的?
【发布时间】:2011-04-15 12:24:49
【问题描述】:

我有一个通用方法:

Func<IEnumerable<T>, bool> CreateFunction<T>()

其中T 可以是任意数量的不同类型。这个方法使用反射做了很多事情,如果TIDictionary,不管字典的TKeyTValue我需要执行字典特定的代码。

所以可以调用该方法:

var f = CreateFunction<string>();
var f0 = CreateFunction<SomePocoType>();
var f1 = CreateFunction<IDictionary<string,object>>();
var f2 = CreateFunction<Dictionary<string,object>>();
var f3 = CreateFunction<SomeDerivedDictionaryType<string,object>>();

等等

根据@Andy 的回答进行澄清

最终我想知道T 是否继承/实现IDictionary,即使T 本身是Dictionary 或派生自该接口的其他类型。

if(typeof(T) == typeof(IDictionary<,>)

不起作用,因为 T 是泛型类型而不是泛型类型定义。

在不知道 TKeyTValue(在编译时不知道)的情况下,我无法与运行时之前知道的任何具体类型进行比较。

我想出的唯一方法是查看类型的名称或使用反射检查其方法,寻找能让我相信它是字典的方法(即寻找 ContainsKeyget_Item )。

有没有直接的方法来做出这种决定?

【问题讨论】:

  • 为什么要将 T 的类型确定为特定的类,而不是为每个类使用重载?
  • 允许的类型集在编译时是未知的。有问题的方法会生成一个 Linq 表达式,该表达式最终要么查询对象的属性,要么在字典的情况下查询集合的内容。
  • 这是用于回答stackoverflow.com/questions/74616/…等问题的技术的一小部分。

标签: c# generics reflection types


【解决方案1】:

您可以使用IsGenericTypeGetGenericTypeDefinition 成员来避免使用丑陋且有潜在风险的类型名称字符串检查,如下所示:

var type = typeof (T);
if (typeof (IDictionary).IsAssignableFrom(type))
{
    //non-generic dictionary
}
else if (type.IsGenericType &&
         type.GetGenericTypeDefinition() == typeof (IDictionary<,>))
{
    //generic dictionary interface
}
else if (type.GetInterfaces().Any(
            i => i.IsGenericType &&
                 i.GetGenericTypeDefinition() == typeof (IDictionary<,>)))
{
    //implements generic dictionary
}

【讨论】:

    【解决方案2】:

    简单的方法就是这样:

    Type iDict = null;
    if (typeof(T).GetGenericTypeDefinition() == typeof(IDictionary<,>))
        iDict = typeof(T);
    else
        iDict = typeof(T).GetInterface(typeof(IDictionary<,>).Name);
    if (iDict != null)
    {
        var genericParams = iDict.GetGenericArguments();
        Type tKey = genericParams[0], tValue = genericParams[1];
    }
    

    请注意,如果T 实现了多个 IDictionary&lt;,&gt; 接口,这将不起作用(抛出异常),但这可能适合您的目的。

    为了完整起见,下面是一个实现,使用第一个接口处理具有多个 IDictionary&lt;,&gt; 接口的类型:

    Type iDict = t.GetType().GetInterfaces()
                  .Where(t => t.IsGenericType
                   && t.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                  .FirstOrDefault();
    if (iDict != null)
    {
        var genericParams = iDict.GetGenericArguments();
        Type tKey = genericParams[0], tValue = genericParams[1];
    }
    

    请注意,在第二个例程中t 是一个对象,而T 在第一个例程中是一个类型。

    【讨论】:

    • 适用于派生词典(Dictionary、SortedDictionary),但不适用于 本身为 IDictionary 时。
    • dkackman:抱歉,我是在假设T 是一个对象的情况下进行操作的。我将其更改为适用于 T 是一种类型的地方。
    【解决方案3】:

    你可以做类似的事情

    class Program
    {
        static void Main(string[] args)
        {
            Example<IDictionary<int, string>>.IsDictionary();
    
            Example<SortedDictionary<int, string>>.IsDictionary();
    
            Example<Dictionary<int, string>>.IsDictionary();
    
            Console.ReadKey();
        }
    }
    
    public class Example<T>
    {
        public static void IsDictionary()
        {
            if (typeof(T).GetInterface(typeof(IDictionary<,>).Name) != null || typeof(T).Name.Contains("IDictionary"))
            {
                Console.WriteLine("Is IDictionary");
            }
            else
            {
                Console.WriteLine("Not IDictionary");
            }
        }
    }
    

    【讨论】:

    • typeof(IDictionary) 无法编译,不幸的是没有类型可以真正实现 IDictionary(没有泛型参数),因此它会返回寻找类型名称。
    • 要编译你必须添加“使用 System.Collections;”因为 IDictionary 是 Dictionary 的接口。您希望检查两个接口,IDictionary 和 IDictionary。提供的代码结果是正确的,并且标识“Is IDictionary”。
    • if 语句的前面验证支持 IDictionary 或 IDictionary 的具体类型,后者评估定义为您正在探测的接口的 T。
    • Doh - 太久了,我忘记了非泛型集合命名空间!
    • 不适用于碰巧包含“IDictionary”的非字典类型,例如:Example.IsDictionary()。请参阅下面的答案。
    【解决方案4】:

    我认为如果你调用 Type.GetGenericTypeDefinition() 应该返回用于构造具体类型的“基本”泛型类型。

    请注意,仅将其与IDictionary&lt;,&gt; 进行比较可能还不够,因为如果有人传入Dictionary&lt;,&gt; 的实例,我假设您也想使用它。您可以检查该类型是否实现了IDictionary&lt;,&gt;,或者您可以调用Type.IsAssignableFrom(),尽管根据文档我不确定这对泛型类型的效果如何。

    【讨论】:

    • 试过 IsAssignableFrom ,你是对的,它不适用于 def 类型。你是对的,部分问题在于知道 T 的一个实例实现了 IDictionary 接口,而不是 T 是否是那个接口类型。
    猜你喜欢
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    相关资源
    最近更新 更多