我正在try 创建一个在泛型ReadOnlySpan<T>上操作的方法,这样我就可以在不更改代码的情况下传递数组或堆栈分配的缓冲区.我还希望将类型T限制为仅为值类型.

该方法如下所示:

void Test1<T>(ReadOnlySpan<T> foo) where T : struct
{
}

但是,如果不传递类型参数,编译器就无法推断类型:

Test1(new[] {42}); // Doesn't compile
Test1<int>(new[] {42}); // Compiles

我也try 了不同的方法,我发现了一些有趣的行为:

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正常工作),我猜测ReadOnlySpanref struct的事实可能是问题的原因.

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

有没有一种方法可以帮助编译器推断ReadOnlySpan in Test1()方法的数据类型?

Update:

我try 显式声明参数的类型:

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

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

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

有谁能更好地解释一下吗?

推荐答案

让我们跟着type inference process号!

在第一阶段,此 case 匹配:

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

将其应用到这种情况下,这将是"new[] {42}具有类型int[],然后从int[]ReadOnlySpan<T>进行下限推断".

如何进行下限推理被指定here.

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

这里Uint[],VReadOnlySpan<T>,而这一步恰恰是出问题的地方.通过 case ,ReadOnlySpan<T>不是T,它也不能为空,所以我们只剩下:

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

在这里,它可以找到"相关的"泛型类型参数和数组组件类型,并对它们进行推断.例如,这是一个它可以用通俗易懂的语言做出的推论:

给定一个List<Foo>的参数和一个IEnumerable<T>的参数,那么T一定是Foo或者它的某个超类(也就是"下限").

在上面的例子中,U₁...Uₑ将是Foo,而V₁...Vₑ将是T.

不幸的是,这些情况都不适用于int[]ReadOnlySpan<T>,因此没有做出任何推断.

这些情况处理数组类型(例如int[]T[]),

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

从数组类型到集合接口的隐式转换(例如,int[]IEnumerable<T>),

V是IEnumerable<V₁>ICollection<V₁>IReadOnlyList<V₁>IReadOnlyCollection<V₁>IList<V₁>中的一个,U是一维数组类型U₁[]

以及相互继承/实现的泛型类型的类型参数(例如,Dictionary<string, int>IDictionary<T, U>).

V是构造的类、 struct 、接口或委托类型C<;V₁...Vₑ>;并且存在唯一的类型C<;U₁...Uₑ>;使得U(或者,如果U是类型参数,则其有效基类或其有效接口集合的任何成员)与C<;U₁...Uₑ>;相同、(直接或间接)继承或实现(直接或间接).

但是,它不判断任何任意的隐式转换,比如从数组到ReadOnlySpan<T>的转换.

这就是说,语言可以被设计成在上面的第二个 case 中添加一个特殊的 case .在接口列表中增加ReadOnlySpan<T>/Span<T>,以及IEnumerable<T>等.让我们希望这在future 得到实施.

Csharp相关问答推荐

MongoDB实体框架核心:表达必须可写

如何在NServicebus中配置学习传输的文件夹(NService bus 8)

有没有办法把+02:00转换成TimeSpan?""

将列表字符串映射为逗号分隔字符串<>

MudBlazor—MudDataGrid—默认过滤器定义不允许用户修改基本过滤器

在C#中,有没有一种方法可以集中定义跨多个方法使用的XML参数描述符?

如何在C#中从正则表达式中匹配一些数字但排除一些常量(也是数字)

在调整大小的控件上绘制

C#阻塞调用或await calling inside calling方法

由于POST中的应用程序/JWT,出现不支持的内容类型异常

C#带主体的主构造函数?

在使用StringBuilder时,如何根据 colored颜色 设置为richTextBox中的特定行着色?

为什么Azure函数(独立工作进程)索引失败?使用Azure App配置的CosmosDbTrigger绑定失败,未解析为值

如何在mediatr命令中访问HttpContext而不安装弃用的nuget包

使用Blazor WebAssembly提高初始页面加载时间的性能

使用可空引用类型时C#接口实现错误

Xamarin Forms应用程序中登录页面的用户名和密码编辑文本之间不需要的空格

在.Net 8 Visual Studio 2022中启用本机AOT发布时发布失败

用于分钟和秒验证的MudTextfield的正则表达式掩码

客户端/服务器RPC如何处理全局变量?