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

时间:2016-02-06 17:00:38

标签: c#

我有一个测试类:

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:无法在ref T行上从x(ref a[I]);转换为'ref byte'”

Intellisense也在行上显示不同的消息:“'Test.x(ref byte)'的最佳重载方法匹配'有一些无效的参数”。有什么想法吗?

我尝试这样做,而不是为每个数组类型编写显式重载,因为我的实际代码对于'标量'类型有大约20个不同的“x”例程,而我不想要20多个函数。

好的调用多个约束是'anded',而不是'orored',但是我得到了没有约束的相同错误。

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

顺便说一句:我是否正确地认为“struct”的约束将与任何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]);
    }
}

2 个答案:

答案 0 :(得分:1)

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()约束。

阅读this MSDN article,解释如何使用通用参数约束。

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

答案 1 :(得分:0)

这里的问题是你从一个通用方法调用非泛型方法并将一个参数T传递给该调用。为了做到这一点,你需要你的内部调用来调用xElement<T>方法(我这样命名,因为你已经使用名称x来整体处理数组,而你的其他方法处理一个仅限数组的元素)。但是你回到了方形1,因为你现在需要在内部方法体中有一个控制流语句(比如switch),这是你想要避免的。

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

更简洁的是,在这个用例中,是的,你将需要一个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] });