我正在尝试创建一种在通用 ReadOnlySpan<T>
上运行的方法,这样我就可以传递数组或堆栈分配的缓冲区,而无需更改代码。我还想限制类型 T
仅作为值类型。
该方法如下所示:
void Test1<T>(ReadOnlySpan<T> foo) where T : struct
{
}
但是,编译器无法在不传递类型参数的情况下推断类型:
Test1(new[] {42}); // Doesn't compile
Test1<int>(new[] {42}); // Compiles
我还尝试了不同的方法,发现了一些有趣的行为:
void Test1<T>(ReadOnlySpan<T> foo) where T : struct
{
}
void Test2<T>(T[] foo) where T : struct
{
}
void Test3<T>(T[] foo)
{
}
void Test4<T>(ReadOnlySpan<T> foo)
{
}
void Test5<T>(List<T> foo) where T : struct
{
}
void Test6<T>(List<T> foo)
{
}
Test1(new[] {42}); // NO
Test2(new[] {42}); // OK
Test3(new[] {42}); // OK
Test4(new[] {42}); // NO
Test5(new List<int> {42}); // OK
Test6(new List<int> {42}); // OK
Test1<int>(new[] {42}); // OK
Test4<int>(new[] {42}); // OK
似乎类型推断仅适用于 *Span
。 s(数组和 List
工作正常),我猜想 ReadOnlySpan
的事实是ref struct
可能是问题的原因。
造成这种不同行为的真正原因是什么?
有没有办法帮助编译器推断 ReadOnlySpan
的数据类型在Test1()
方法?
更新:
我尝试显式声明参数的类型:
ReadOnlySpan<int> testSpan = new[] {42};
Test1(testSpan); // OK!
Span<int> test = new[] {42};
Test1(test); // NO
看来这里真正的问题是隐式转换。
有谁能解释一下吗?
最佳答案
让我们关注type inference process !
在第一阶段,此案例匹配:
if Eᵢ has a type U and the corresponding parameter is a value parameter (§15.6.2.2) then a lower-bound inference is made from U to Tᵢ.
将其应用于本例,即“new[] {42}
具有类型 int[]
,然后从 int[]
到 ReadOnlySpan<T>
进行下限推断”。
指定如何进行下限推理 here .
A lower-bound inference from a type U to a type V is made as follows:
这里U
是 int[]
和V
是 ReadOnlySpan<T>
,而这一步正是出错的地方。浏览案例,ReadOnlySpan<T>
不是T
,它也不能为空,所以我们要做的是:
Otherwise, sets U₁...Uₑ and V₁...Vₑ are determined by checking if any of the following cases apply:
在这里它找到“相关的”泛型类型参数和数组组件类型,并对它们进行推断。例如,它可以用简单的语言做出以下推论:
given a
List<Foo>
argument, and aIEnumerable<T>
parameter, thenT
must beFoo
or some superclass of it (aka a "lower bound").
在上述情况下,U₁...Uₑ 将为 Foo
,而 V₁...Vₑ 将为 T
.
不幸的是,这些情况都不适用于 int[]
和ReadOnlySpan<T>
,所以不做任何推断。
这些情况处理数组类型(例如 int[]
到 T[]
),
V is an array type V₁[...]and U is an array type U₁[...] of the same rank
从数组类型到集合接口(interface)的隐式转换(例如 int[]
到 IEnumerable<T>
),
V is one of
IEnumerable<V₁>
,ICollection<V₁>
,IReadOnlyList<V₁>
,IReadOnlyCollection<V₁>
orIList<V₁>
and U is a single-dimensional array type U₁[]
以及相互继承/实现的泛型类型的类型参数(例如 Dictionary<string, int>
到 IDictionary<T, U>
)。
V is a constructed class, struct, interface or delegate type C<V₁...Vₑ> and there is a unique type C<U₁...Uₑ> such that U (or, if U is a type parameter, its effective base class or any member of its effective interface set) is identical to, inherits from (directly or indirectly), or implements (directly or indirectly) C<U₁...Uₑ>.
它不会检查任何任意隐式转换,例如从数组到 ReadOnlySpan<T>
的转换。然而。
也就是说,该语言可以被设计为在上面的第二种情况中添加一个特殊情况。添加ReadOnlySpan<T>
/Span<T>
到接口(interface)列表,除了IEnumerable<T>
和别的。我们希望这在未来能够实现。
关于c# - C# 中使用 ReadOnlySpan 进行泛型类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77577043/