为什么零长度 stackalloc 会让 C# 编译器乐意允许条件 stackalloc?

2024-05-01

下面的“修复”让我很困惑;这里的场景是根据大小有条件地决定是否使用堆栈还是租用缓冲区 - 然而,这是一个相当小众但有时必要的优化:使用“明显”实现(数字 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);
    }
}

The Span<T> / ref特性本质上是一系列关于给定值可以通过值或引用转义到哪个范围的规则。虽然这是根据方法范围编写的,但将其简化为以下两个语句之一会很有帮助:

  1. 该值无法从方法返回
  2. 该值可以从方法中返回

The 跨度安全文档 https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/span-safety.md详细介绍了如何计算各种语句和表达式的范围。这里的相关部分是如何locals https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/span-safety.md#locals被处理。

主要的一点是,当地人是否可以返回是在当地申报时间计算的。在声明局部变量时,编译器会检查初始值设定项并决定局部变量是否可以从方法返回。在存在初始化器的情况下,如果能够返回初始化表达式,则本地将能够返回。

如何处理声明了本地变量但没有初始化程序的情况?编译器必须做出决定:是否可以返回?在设计该功能时,我们决定默认设置为“可以返回”,因为该决定对现有模式造成的摩擦最少。

这确实给我们带来了一个问题:开发人员如何声明一个不能安全返回但也缺少初始化程序的本地变量。最终我们确定了这样的模式= stackalloc [0]。这是一个可以安全优化的表达式,也是一个强有力的指标,基本上是一个要求,表明本地返回是不安全的。

知道这可以解释您所看到的行为:

  • Span<int> s = stackalloc[0]:返回不安全,因此稍后返回stackalloc成功
  • Span<int> s = default:可以安全返回,因为default可以安全返回。这意味着以后stackalloc失败,因为您分配的值不安全返回到标记为安全返回的本地
  • Span<int> s;:返回是安全的,因为这是未初始化的本地变量的默认值。这意味着以后stackalloc失败,因为您分配的值不安全返回到标记为安全返回的本地

真正的缺点是= stackalloc[0]方法是它只适用于Span<T>。这不是一个通用的解决方案ref struct。但实际上,对于其他类型来说这并不是什么大问题。关于我们如何做到这一点有一些猜测更一般 https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md#allowing-attributes-on-locals但目前还没有足够的证据证明这样做是合理的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么零长度 stackalloc 会让 C# 编译器乐意允许条件 stackalloc? 的相关文章

  • Volatile.Read 和 Volatile.Write 背后的逻辑是什么?

    来自 MSDN Volatile Read 读取字段的值 在需要它的系统上 插入一个 阻止处理器重新排序内存的内存屏障 操作如下 如果在该方法之后出现读或写 代码 处理器无法移动它before这个方法 and Volatile Write
  • 元组在 VS2012 中如何工作?

    Visual Studio 2012 功能 tuples但不是可变参数模板 这是如何完成的 如何在不使用可变模板的情况下实现元组 简而言之 微软做了与之前在 NET 中实现类似元组的数据类型完全相同的事情 创建许多版本 每个版本都有固定数量
  • IEnumerable 的 String.Join(string, string[]) 的类似物

    class String包含非常有用的方法 String Join string string 它从数组创建一个字符串 用给定的符号分隔数组的每个元素 但一般来说 它不会在最后一个元素之后添加分隔符 我将它用于 ASP NET 编码 以用
  • 锁定 ASP.NET 应用程序变量

    我在 ASP NET 应用程序中使用第三方 Web 服务 对第 3 方 Web 服务的调用必须同步 但 ASP NET 显然是多线程的 并且可能会发出多个页面请求 从而导致对第 3 方 Web 服务的同时调用 对 Web 服务的调用封装在自
  • C# 处理标准输入

    我目前正在尝试通过命令行断开与网络文件夹的连接 并使用以下代码 System Diagnostics Process process2 new System Diagnostics Process System Diagnostics Pr
  • C++ 将联合强制转换为其成员类型之一

    以下对我来说似乎完全符合逻辑 但不是有效的 C 联合不能隐式转换为其成员类型之一 有人知道为什么不这样做的充分理由吗 union u int i char c function f int i int main u v v i 6 f v
  • 将日期时间转换为指定格式

    我有这个日期格式yy MM dd HH mm ss ex 12 02 21 10 56 09 问题是 当我尝试使用以下代码将其转换为不同格式时 CDate 12 02 21 10 56 09 ToString MMM dd yyyy HH
  • 有什么方法可以重载 C# 中的扩展方法吗?

    我有以下模型模式 public abstract class PARENTCLASS public class CHILD A CLASS PARENTCLASS public static class EXTENSION public s
  • 你好,我最近正在开发我的新游戏,我遇到了*无限跳跃*的问题

    所以基本上当我按跳跃 空格键时我会跳跃但是如果我连续按空格键它 只是跳啊跳啊跳等等 我不想要我只想它跳一次 code if Input GetKeyDown space isGrounded velocity y Mathf Sqrt ju
  • 使用多线程进行矩阵乘法?

    我应该使用线程将两个矩阵相乘 有两件事 当我运行程序时 我不断得到 0 我还收到消息错误 对于每个错误 它在粗体行上显示 警告 从不兼容的指针类型传递 printMatrix 的参数1 我尝试打印输出 还要注意 第一个粗体块 这是我解决问题
  • doxygen c++:记录由“using”声明公开的私有继承成员

    作为一个例子 我有以下课程 class A public void methodOne class B private A public Brief description using A methodOne 我还没有找到强制 doxyge
  • C# 中的 C/C++ 代码编译器

    在 C 中 我可以使用下面的代码编译 VB 和 C 代码 但无法编译 C C 代码 有什么办法可以做到这一点吗 C 编译器 public void Compile string ToCompile string Result null st
  • 无法在 C# 中为 EventArgs 分配使用派生类型的事件处理程序

    所以我有一个事件声明如下 public event EventHandler OnChangeDetected 然后我有以下处理程序被分配给该事件 myObject OnChangeDetected OnTableChanged 我的理解是
  • C 中使用 getrandom 实现随机浮点数

    我试图生成一个介于 0 和 1 之间的随机浮点数 无论是在 0 1 还是 0 1 对我来说都不重要 网上关于此的每个问题似乎都涉及rand 呼叫 播种time NULL 但我希望能够每秒多次调用我的程序 并每次都获得不同的随机数 这引导我找
  • Xamarin.Forms UWP 项目中标题栏和选项卡之间令人恼火的空白

    我几乎是新手Xamarin Forms我正在开发一个相当简单的跨平台应用程序 该应用程序在 Android 中显示得足够好 但在 UWP 中却出现了一个愚蠢的空白 该项目由一个 TabbedPage 组成 其中包含 4 个 Navigati
  • 如果“嵌入式”SQL 2008 数据库文件不存在,如何创建它?

    我使用 C ADO Net 和在 Server Management Studio 中创建的嵌入式 MS SQL 2008 数据库文件 附加到 MS SQL 2008 Express 创建了一个数据库应用程序 有人可以向我指出一个资源 该资
  • 将 Swagger 与命名空间版本的 WebApi 结合使用

    我已经找到了如何使用基于名称空间的 WebAPI 版本这个班 https aspnet codeplex com SourceControl changeset view dd207952fa86 Samples WebApi Namesp
  • 如何在c linux中收听特定接口上的广播?

    我目前可以通过执行以下操作来收听我编写的简单广播服务器 仅广播 hello int fd socket PF INET SOCK DGRAM 0 struct sockaddr in addr memset addr 0 sizeof ad
  • 如何提高环复杂度?

    对于具有大量决策语句 包括 if while for 语句 的方法 循环复杂度会很高 那么我们该如何改进呢 我正在处理一个大项目 我应该减少 CC gt 10 的方法的 CC 并且有很多方法都存在这个问题 下面我将列出一些例如我遇到的问题的
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost

随机推荐