2016-07-04 26 views
4

注意这个问题是关于技术方面,而不是设计决策。回答或评论不同的设计不是回答这个问题,因为我只对这个特定问题的技术方面的性质感兴趣。GenericTypeDefinitionName自IEnumerable <object>

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(); 
    ... 
} 

也可以工作,但后来我意识到:“嘿!为什么不只是return typeof(T).Name'

事实证明,它返回字符串"Object",而我期望与以前的实施相同的结果(例如"MyClass")。

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

+0

你能告诉你究竟是如何把它称为使'的typeof(T).Name'返回' “对象”'? –

+0

你是否在使用通用方差? 'IEnumerable '可以被分配给'IEnumerable '。 –

+0

@RenéVogt我编辑了这个问题来展示如何和在哪里调用扩展方法。 – QuantumHive

回答

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 
+0

都会写在控制台的实际类型? –

+0

@Ehsan我已经在编辑 –

+0

的示例中添加了控制台输出,我想我明白了。因此,在我的问题中,'MyMethod'在其参数中定义了一个编译时类型('IEnumerable '),'GetByGenerics'例子永远不会知道基于开放泛型类型'T'的原始类型('IEnumerable '') ,对吗?因为这是在编译时定义的,并且不能由编译器推断出来? – QuantumHive

相关问题