来自IEnumerable <object>的GenericTypeDefinitionName

时间:2016-07-04 10:24:47

标签: c# .net generics reflection

注意此问题涉及技术方面,关于设计决策。回答或评论不同的设计回答这个问题,因为我只对这个特定问题的技术方面的性质感兴趣。

C# 6.0我有这个方法我传递的是IEnumerable<T>

public void MyMethod(IEnumerable<object> list) { ... }

让我们说调用者在MyClass[]数组上调用它 我想要的是泛型类型定义的类名,我有这个方法的实现:

public void MyMethod(IEnumerable<object> list)
{
    ...
    var name =
        from abstraction in list.GetType().GetInterfaces()
        where abstraction.IsGenericType
        && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>)
        from genericArgumentType in abstraction.GetGenericArguments()
        select genericArgumentType.Name;
    ...
}

现在在这种情况下(在方法体内),这正确地返回类的名称(例如字符串"MyClass")。所以我试着将它重构为一个开放的通用扩展方法,如下所示:

public static string GetGenericTypeDefinitionName<T>(this IEnumerable<T> list)
{
    var name =
        from abstraction in list.GetType().GetInterfaces()
        where abstraction.IsGenericType
        && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>)
        from genericArgumentType in abstraction.GetGenericArguments()
        select genericArgumentType.Name;

    return name.Single();
}

然后从GetGenericTypeDefinitionName()内拨打MyMethod(),如下所示:

public void MyMethod(IEnumerable<object> list)
{
    ...
    var name = list.GetGenericTypeDefinitionName();
    ...
}

哪个也有效,但后来我意识到:&#39; 嘿!为什么不 return typeof(T).Name &#39;

结果发现它返回字符串"Object",而我期望与之前的实现相同的结果(例如"MyClass")。

甚至可以获得预期的类型?在处理开放泛型类型T时,似乎所有信息都丢失了。 为什么我没有达到预期的类型? C#的这种特殊行为的技术细节是什么?

1 个答案:

答案 0 :(得分:5)

如果您使用通用差异将某个引用类型IEnumerable<T> T分配给IEnumerable<object>,则会发生您所描述的内容。

编译器将使用它知道的内容在编译时选择<T>,在这种情况下为object。但是,如果使用反射,则会得到原始名称 - 因为实际对象不会因方差技巧而改变。

static void Main()
{
    IEnumerable<Foo> typed = new Foo[0];
    IEnumerable<object> untyped = typed;

    Console.WriteLine(typed.GetByGenerics());     // Foo
    Console.WriteLine(untyped.GetByGenerics());   // Object

    Console.WriteLine(typed.GetByReflection());   // Foo
    Console.WriteLine(untyped.GetByReflection()); // Foo
}
public static string GetByGenerics<T>(this IEnumerable<T> list)
{
    return typeof(T).Name;
}
public static string GetByReflection<T>(this IEnumerable<T> list)
{
    var name =
        from abstraction in list.GetType().GetInterfaces()
        where abstraction.IsGenericType
        && abstraction.GetGenericTypeDefinition() == typeof(IEnumerable<>)
        from genericArgumentType in abstraction.GetGenericArguments()
        select genericArgumentType.Name;

    return name.Single();
}

这是因为您实际上正在调用

Console.WriteLine(GetByGenerics<Foo>(typed));      // Foo
Console.WriteLine(GetByGenerics<Object>(untyped)); // Object