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

标签 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 上?语言是否有任何差异或可能导致这种情况的原因?

最佳答案

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

6.2.7 Explicit conversions involving type parameters
The following explicit conversions exist for a given type parameter T:
• From the effective base class C of T to T and from any base class of C to T. At run-time, if T is a value type, the conversion is executed as an unboxing conversion. Otherwise, the conversion is executed as an explicit reference conversion or identity conversion.

[…there are three other bullet points, but none apply here…]

The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear.



无约束,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 时,您不能从非基类 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不同,因此在每种情况下都会产生不同的结果。

查看相关问题:

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

我很失望,这些其他相关的问题和答案都没有解决问题的核心,这就是 C# 提出这个要求的原因。我已经尝试在上面这样做。但是,它们都涵盖了类似的场景,展示了在非 Winrt 代码的上下文中如何发生同样的事情。它们还说明了两种基本解决方案:
  • 对泛型参数使用约束(我在此建议)
  • 投给 object在转换到 T 之前.这绕过了通用转换,删除了上下文,以便编译器允许转换。恕我直言,这不太可取;但是如果由于某种原因无法添加约束(例如,您正在处理一种不适用于所有可能的 T 的特殊情况……无论这种实现是多么不可取),这将起作用。
  • 关于c# - 无法将类型 'System.Attribute' 转换为泛型参数 'T' - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35494072/

    相关文章:

    c# - 与 RazorEngine 同时发送电子邮件时出错

    c# - 数组是否对可编程图形管道的顶点元素数据有效?

    c++ - 为什么这段代码在第31行显示cout << x1 << x2;

    java - 为什么我收到错误: cannot find symbol for JComponent?

    windows-runtime - WiFiDirectDevice::FromIdAsync 抛出 Win32 控制台应用程序

    c# - 如何从代码中清除 Windows 8.1 WebView 的缓存?

    c# - 如何使用 WPF XAML 构造堆叠表单?

    c# - 使用反射获取通用列表始终为空

    Javac “cannot find symbol” 为什么?

    .net - WinRT 动画不显示中间值