无法将类型“系统属性”转换为泛型参数“T” - 为什么
本文关键字:为什么 泛型 参数 转换 系统属性 类型 系统 属性 | 更新日期: 2023-09-27 18:35:14
我正在尝试将我的桌面.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 上?语言是否有任何差异或可能导致这种情况的原因?
这是 C# 中类型转换规则的结果。从 C# 5.0 规范:
6.2.7 涉及类型参数
的显式转换 给定类型参数 T 存在以下显式转换:
• 从 T 的有效基类 C 到 T 以及从 C 到 T 的任何基类。在运行时,如果 T 是值类型,则转换将作为拆箱转换执行。否则,转换将作为显式引用转换或标识转换执行。
[...还有其他三个要点,但在这里都不适用...]
上述规则不允许从不受约束的类型参数直接显式转换为非接口类型,这可能会令人惊讶。此规则的原因是防止混淆并使此类转换的语义清晰。
不受约束,唯一已知的T
基类是 System.Object
。但在 Winrt 中,GetCustomAttributes()
方法作为扩展方法实现,返回IEnumerable<Attribute>
而不是在 .NET API 中返回的object[]
。因此变量attr
的类型为 Attribute
,而不是 object
。
因此,当您使用 .NET API 时,可以从基类object
强制转换为T
,但是当您使用 Winrt 时,您无法从非基类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
转换为 T
。
请注意,这在 .NET 和 Winrt 之间的语言实现上没有区别。这是完全相同的 C# 规则。只是您实际上正在处理不同的GetCustomeAttributes()
实现,其中返回值不同,从而使变量的类型attr
不同,从而在每种情况下产生不同的结果。
请参阅相关问题:
无法将类型"System.Windows.Forms.Control"转换为"T"
泛型
的 C# 问题在 c#
中强制转换为泛型类型失败
令我失望的是,这些其他相关的问题和答案都没有解决问题的核心,这就是 C# 提出此要求的原因。我在上面尝试过这样做。但是,它们都涵盖了类似的方案,显示了在非 Winrt 代码的上下文中如何发生完全相同的事情。它们还说明了两个基本解决方案:
- 对泛型参数使用约束(正如我在这里建议的那样)
- 投射到
object
,然后再投射到T
。这将绕过泛型转换,删除上下文,以便编译器允许强制转换。恕我直言,这不太可取;但是,如果您由于某种原因无法添加约束(例如您正在处理不适用于所有可能T
的特殊情况......无论此类实现多么不可取),这将起作用。