下面的“修复”让我很困惑;这里的场景是根据大小有条件地决定是否使用堆栈还是租用缓冲区 - 然而,这是一个相当小众但有时必要的优化:使用“明显”实现(数字 3,推迟明确的分配,直到我们真正想要分配它),编译器抱怨 CS8353:
类型为“Span”的 stackalloc 表达式的结果不能在此上下文中使用,因为它可能会暴露在包含方法之外
简短的重现(完整的重现如下)是:
// take your pick of:
// Span<int> s = stackalloc[0]; // works
// Span<int> s = default; // fails
// Span<int> s; // fails
if (condition)
{ // CS8353 happens here
s = stackalloc int[size];
}
else
{
s = // some other expression
}
// use s here
我在这里唯一能想到的是编译器是really标记为stackalloc
正在逃避其中的上下文stackalloc
发生了,并且挥舞着旗帜说“我无法证明这在该方法的后面是否是安全的”,但是通过让stackalloc[0]
一开始,我们将“危险”上下文作用域推得更高,现在编译器很高兴它永远不会逃脱“危险”作用域(即它永远不会真正离开该方法,因为我们在顶部作用域声明) 。这种理解是否正确,这只是编译器在可证明方面的限制?
What's really(对我来说)有趣的是= stackalloc[0]
基本上是一个空操作anyway, 意思是至少以编译的形式工作号码1= stackalloc[0]
与失败的数字 2 相同= default
.
完整重现(也可在 SharpLab 上查看 IL https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBLANgHwAEAmARgFgAoQgBgAJDSA6AIQFcAzDmKAZwG4qVQgGYGpJA2J0AwlQDeVOsoZjGkwijoBlDAEMwAawCCuXBDAAxNgDsAFNhsY6kWxgCUSlYsoq/dAHoAugBiUjoMAAtsXjoYulwYAHM9XDQ6ACs2XmdHbAxsVOwALxhY2L0KugATGA49NlxnXgAHPRsvfx02mwAeRwwAPjpYgF4R/SNU8zA4pwBtGgBdQV8uuk7/INDpKPj9s2TUkDoeKGhZbQAOEQBWERPjOlheRucIDjo9CYNDaYtTggWi9eNgIB01l1th8IgBPFowOgAcm0PX6TkGSJc7RsEGcwER2Rg1TmEWisUgThgCHxMDADV4iPydAAtnpYZs/NsCYCWhBGSSIGwMKDanQYVFEZT9HkbElWTAohBqpyVNtUe10UMRnRxrV6m9VusNpCtsEQmJKnFYntYnZUrwIHFDik0nFak5sPTcKcoOcoJ5TVzghq+gNhgJVcoo3MMPMluKAG48UGlEnjGyNXBG9YYKAcoPeGN+bCfOyuJx0Xp0ETEQPGvzyNXBGTXO5iSJ6FoImwVZy2n5TMwWYvrMaDv7D2YDeYVjArUfKAC+i9OuEZq58DfWEGTfBKxN1dGMfvZAAUIBBcFrBkxtJ3YNUmAAlGBOctCpzuHPbvzjmwwAA7t0mrhnYu4pge1TpDQ6Rzt+q4roWXQXi0jR6BgMB2LwCHIf4AAiEDaBALKKtEcoAOr5JE2G4caSHGhwjjTAWxpbr+pZ0OBe6poe8S4s4mZmPWv7sb+KgnlA56Xte4Z3g+xIvoqbBQPYEH7mmdG/gx6w6cuQimsC2CJhhiLqAwWioehmF2KGN4jCJXSjMMUTnMBAHAQAcniACSLItIkpFUtUACiCBgDALQFOCdjfoEwSAZ2mF7psRkmZh4gaFohHEaReyUdRdivno1QAPI2LgsJ2eGDkxs5ZJuXQHl0N5GB+QFMBBZhoXhZF0X2HF2yJaZKWmps2wCTAJz3hAgHeaVLQABpfDYJIzXNEALQAmi4JEtHgTIegU3qVZk2RYONwS4NUMx6EwsSwXM+QQMAGQgWGGLpNdMxMDQmzmZoOiRLN81LbFmxif41UYjq4w5L8/zTgsyw/gRREkWRjhJFRUS0ajemZRZQMg5tLRbeDpqQ340PauO+oNE0qN+DlGP5djhU4fjVBLkAA=).
using System;
using System.Buffers;
public static class C
{
public static void StackAllocFun(int count)
{
// #1 this is legal, just initializes s as a default span
Span<int> s = stackalloc int[0];
// #2 this is illegal: error CS8353: A result of a stackalloc expression
// of type 'Span<int>' cannot be used in this context because it may
// be exposed outside of the containing method
// Span<int> s = default;
// #3 as is this (also illegal, identical error)
// Span<int> s;
int[] oversized = null;
try
{
if (count < 32)
{ // CS8353 happens at this stackalloc
s = stackalloc int[count];
}
else
{
oversized = ArrayPool<int>.Shared.Rent(count);
s = new Span<int>(oversized, 0, count);
}
Populate(s);
DoSomethingWith(s);
}
finally
{
if (oversized is not null)
{
ArrayPool<int>.Shared.Return(oversized);
}
}
}
private static void Populate(Span<int> s)
=> throw new NotImplementedException(); // whatever
private static void DoSomethingWith(ReadOnlySpan<int> s)
=> throw new NotImplementedException(); // whatever
// note: ShowNoOpX and ShowNoOpY compile identically just:
// ldloca.s 0, initobj Span<int>, ldloc.0
static void ShowNoOpX()
{
Span<int> s = stackalloc int[0];
DoSomethingWith(s);
}
static void ShowNoOpY()
{
Span<int> s = default;
DoSomethingWith(s);
}
}