无法将类型“系统属性”转换为泛型参数“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 上?语言是否有任何差异或可能导致这种情况的原因?

无法将类型“系统属性”转换为泛型参数“T” - 为什么

这是 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 代码的上下文中如何发生完全相同的事情。它们还说明了两个基本解决方案:

  1. 对泛型参数使用约束(正如我在这里建议的那样)
  2. 投射到object,然后再投射到T。这将绕过泛型转换,删除上下文,以便编译器允许强制转换。恕我直言,这不太可取;但是,如果您由于某种原因无法添加约束(例如您正在处理不适用于所有可能T的特殊情况......无论此类实现多么不可取),这将起作用。