模板化函数参数上的意外错误:“无法从'ref T'转换为'ref byte'”

本文关键字:ref 无法从 byte 转换 参数 函数 错误 意外 | 更新日期: 2023-09-27 18:33:33

我有一个测试类:

class Test
{
    void x(ref byte v) { }
    void x(ref sbyte v) { }
    void x<T>(ref T[] a) where T: byte, sbyte
    {
        for (int i = 0; i < a.Length; ++i)
            x(ref a[i]);
    }
}

在"错误列表"窗口中,我收到错误

">参数 1:无法在行x(ref a[I]);上从 ref T 转换为'ref byte'">

Intellisense 还在行上显示一条不同的消息:"'Test.x(ref byte(' 的最佳重载方法匹配有一些无效的参数"。知道为什么吗?

我尝试这样做,而不是为每个数组类型编写显式重载,因为我的实际代码有大约 20 个不同的"x"例程用于"标量"类型,我不想再增加 20 个函数。

对多个约束的良好调用是"anded",而不是"ored",但是在没有约束的情况下我得到了相同的错误。

我理解为什么不需要第三次重载中的"ref",但这个接口最终可能会重新分配数组,然后需要一个 ref。

顺便说一句:我认为对"结构"的约束将与任何 ValueType(包括基元类型(匹配是否正确?

这是现在的代码,具有相同的错误:

class Test
{
    void x(ref byte v) { }
    void x(ref sbyte v) { }
    void x<T>(T[] a)
    {
        for (int i = 0; i < a.Length; ++i)
            x(ref a[i]);
    }
}

模板化函数参数上的意外错误:“无法从'ref T'转换为'ref byte'”

roslyn 给出的确切错误消息是:

(8,34,8,38): Error CS0701: 'byte' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
(8,40,8,45): Error CS0701: 'sbyte' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
(11,18,11,22): Error CS1503: Argument 1: cannot convert from 'ref T' to 'ref byte'

bytesbyte 不是泛型参数的有效约束。在 C# 中,只能使用 BaseClass、Interface、引用类型、值类型和 new(( 约束。

阅读这篇 MSDN 文章,其中介绍了如何使用泛型参数约束。

对于真正感兴趣的人,编译器生成了第三条编译消息,因为它试图通过忽略参数T上的无效约束来从错误中恢复。没有任何约束,使用 T 作为 byte 是不安全的。

这里的问题是你正在从泛型方法调用一个非泛型方法,并将 T 参数传递给该调用。 为此,您需要内部调用来调用 xElement<T> 方法(我以这种方式命名它,因为您已经使用 name x 来处理整个数组,而您的其他方法仅处理数组的一个元素(。 但是你又回到了方程式 1,因为你现在需要在内部方法主体中有一个控制流语句(如 switch(,这是你试图避免的。

这里真正的问题是模板和泛型不是一回事。 第一个告诉编译器为编译时实际检测到的每种类型创建方法的副本。 第二个告诉运行时根据遇到的类型查找(如果使用反射,则进行 JIT 编译(方法的版本。 由于您只为字节和字节提供了(固定的,而不是泛型的(版本,因此编译器立即知道它无法处理运行时可能遇到的"泛型"情况。

更简洁地说,在这个用例中,是的,您将需要一个 switch 语句,因为您尝试使用它的意义上真正的模板不可用。

还有另一种选择,顺便说一句,您可以自己使用反射来查找方法:

typeof(Test)
// Get all public instance methods
   .GetMethods(BindingFlags.Public|
        BindingFlags.Instance|
        BindingFlags.DeclaredOnly)
// Search by name and by parameter type
   .Where(mi => mi.Name == "xElement" && 
        mi.GetParameters()[0].ParameterType = typeof(T))
// Take the first one found
   .First()
// Call it
   .Invoke(this, new object[] { a[i] });