C# 中使用 ReadOnlySpan 进行泛型类型推断

问题描述 投票:0回答:1

我正在尝试创建一个在通用

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
无法正常工作(数组和
List
可以正常工作),我猜
ReadOnlySpan
ref struct
的事实可能是导致问题。

造成这种不同行为的真正原因是什么?

有没有办法帮助编译器推断

ReadOnlySpan
方法中
Test1()
的数据类型?

更新:

我尝试显式声明参数的类型:

ReadOnlySpan<int> testSpan = new[] {42};
Test1(testSpan); // OK!

Span<int> test = new[] {42};
Test1(test); // NO

看来这里真正的问题是隐式转换。

谁能更好地解释一下吗?

c# .net-core type-inference
1个回答
0
投票

让我们跟随类型推理过程

在第一阶段,此案例符合:

如果 Eᵢ 具有类型 U 并且相应的参数是值参数 (§15.6.2.2),则从 U 到 Tᵢ 进行下限推断。

将其应用到这种情况,那就是“

new[] {42}
具有类型
int[]
,然后从
int[]
ReadOnlySpan<T>
进行下限推断”。

此处指定了如何进行下限推理。

从类型 U 到类型 V 的下界推断如下:

这里
U

int[]
V
ReadOnlySpan<T>
,而这一步正是出错的地方。通过这些案例,
ReadOnlySpan<T>
不是
T
,它也不能为空,所以我们可以:

否则,通过检查是否适用以下任何情况来确定集合 U₁...Uₑ 和 V₁...Vₑ:

这是它做出如下推论的地方:

给定一个
List<Foo>

参数和

IEnumerable<T>
参数,那么
T
必须是
Foo
或其某个超类(也称为“下界”)。

在上述情况下,

U₁...Uₑ 将是
Foo

,V₁...Vₑ 将是

T
不幸的是,这些情况都不适用,也没有做出任何推论。这些情况实际上考虑了很多事情 - 它处理数组类型(例如 

int[]

T[]
),

V 是数组类型 V₁[...],U 是相同秩的数组类型 U₁[...]

从数组类型到集合接口的隐式转换(例如
int[]

IEnumerable<T>
),

V 是
IEnumerable<V₁>

ICollection<V₁>
IReadOnlyList<V₁>
IReadOnlyCollection<V₁>
IList<V₁>
之一,U 是单维数组类型 U₁[]

以及相互继承/实现的泛型类型的类型参数(例如
List<int>

IList<T>
)。

V 是构造类、结构、接口或委托类型 C
,并且存在唯一类型 C

,使得 U(或者,如果 U 是类型参数,则其有效基类或其有效接口集的任何成员)是与 C. 相同、继承自(直接或间接)或实现(直接或间接)C

但是,它不会检查任何任意隐式转换。

也就是说,该语言可以被设计为在上面的第二种情况中添加一个特殊情况。除了

ReadOnlySpan<T>

 等之外,还将 
Span<T>
/
IEnumerable<T>
 添加到界面列表中。让我们希望这在未来能够实现。

© www.soinside.com 2019 - 2024. All rights reserved.