无法将类型'System.Attribute'转换为泛型参数'T' - 为什么?

时间:2016-02-18 22:51:23

标签: c# compiler-errors windows-runtime windows-8.1 system.reflection

我正在尝试将桌面.NET dll编译为WinRT平台(目标Windows 8.1)。它在桌面上工作正常但在WinRT项目中我在代码中有编译器错误:

internal static T[] CreateRuntime<T>(MemberInfo member, bool inherit)
{
    return member.GetCustomAttributes(typeof(T), inherit).Select(attr => (T)attr).ToArray();
}

我无法将Attribute attr转换为通用参数T ...但为什么只能在WinRT上?语言有什么不同或可能导致什么原因?

1 个答案:

答案 0 :(得分:2)

这是C#中类型转换规则的结果。来自C#5.0规范:

  

6.2.7涉及类型参数的显式转换
  对于给定的类型参数T,存在以下显式转换:
  •从T的有效基类C到T以及从C到T的任何基类。在运行时,如果T是值类型,则转换将作为拆箱转换执行。否则,转换将作为显式引用转换或标识转换执行。

   [......还有其他三个要点,但这里没有一个要点......]

  上述规则不允许从无约束类型参数直接显式转换为非接口类型,这可能是令人惊讶的。这条规则的原因是为了防止混淆并使这种转换的语义清晰。

无约束,T唯一已知的基类是System.Object。但在Winrt中,使用GetCustomAttributes()方法is implemented as an extension method,返回IEnumerable<Attribute>而不是返回的object[] in the .NET API。因此,变量attr的类型为Attribute,而不是object

因此,当您使用.NET API时,可以从基类object转换为T,但是当您使用Winrt时,您无法从非基类转换{ {1}}至Attribute

您可以通过向方法声明添加约束来更改T的已知基类:

T

现在基类internal static T[] CreateRuntime<T>(MemberInfo member, bool inherit) where T : Attribute { return member.GetCustomAttributes(typeof(T), inherit).Select(attr => (T)attr).ToArray(); } 被声明为T,您现在可以从Attribute投射到Attribute


请注意,这与 .NET和Winrt之间的语言实现存在差异。这与C#规则完全相同。简单地说,您实际上正在处理T的不同实现,其中返回值不同,使得变量GetCustomeAttributes()的类型不同,从而在每种情况下产生不同的结果。


查看相关问题:

Cannot convert type 'System.Windows.Forms.Control' to 'T'
C# Problem with Generics
Casting to generic type fails in c#

我很失望这些与其他相关的问题和答案都没有解决问题的核心,这就是为什么 C#提出这个要求。我试过这样做。但是,它们都涵盖了类似的场景,展示了在非Winrt代码的上下文中如何发生同样的事情。他们还说明了两个基本解决方案:

  1. 对泛型参数使用约束(我在此建议)
  2. 转换为attr,然后转换为object。这绕过了泛型转换,删除了上下文,以便编译器允许转换。恕我直言,这是不太可取的;但是如果由于某些原因你不能添加约束(比如你正在处理一个不适用于所有可能的T的特殊情况......但是不合适的这种实现是),这将有效。