下面的“搞定”对我来说很迷惑;这里的场景是根据大小有条件地决定是否使用堆栈还是租用缓冲区--这是一个相当小的小范围,但有时--必要的优化,然而:对于“明显的”实现(第3条,推迟确定的赋值,直到我们真正想要赋值),编译器向CS8353抱怨:
在此上下文中不能使用“span
简短的复制品(完整的复制品如下)是:
// 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
这里我唯一能想到的是,编译器实际上是在标记stackalloc
正在转义stackalloc
发生的上下文,并且在挥舞一个标记说“我无法证明这在方法的后面是否会是安全的”,但是通过在开始时使用stackalloc[0]
,我们将“危险”上下文范围推高,现在编译器很高兴它从未转义“危险”范围(即它从未离开方法,因为我们在顶部范围声明)。这种理解是正确的吗,就可以证明的内容而言,它只是一个编译器的限制?
(对我来说)真正有趣的是,=stackalloc[0]
基本上是一个no-op,这意味着至少在编译的形式下,工作号1=stackalloc[0]
与失败号2=default
是相同的。
完全再现(也可在SharpLab上查看IL)。
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);
}
}
span
特性本质上是一系列规则,给定值可以通过值或引用转义到哪个作用域。虽然这是根据方法范围编写的,但简化为两个语句中的一个很有帮助:
span安全文档详细介绍了如何为各种语句和表达式计算作用域。这里的相关部分是如何处理本地数据的。
主要带走的是,一个本地能否返回是在本地申报时间计算的。在声明本地时,编译器检查初始值设定项,并决定是否可以从方法返回本地值。如果有一个初始化器,那么如果能够返回初始化表达式,则本地将能够返回。
如何处理声明了局部值但没有初始值设定项的情况?编译器要做一个决定:它能返回还是不能返回?在设计特性时,我们决定默认值为“It can be return”,因为这是对现有模式造成最小摩擦的决定。
这确实给我们留下了一个问题,即开发人员如何声明一个返回时不安全但又缺少初始化器的本地。最终我们确定了=stackalloc[0]
的模式。这是一个可以安全优化的表达式,也是一个很强的指标,基本上是一个要求,表明本地返回时不安全。
知道这解释了你所看到的行为:
span
s=stackalloc[0]
:返回该值不安全,因此后面的stackalloc
成功span
s=default
:返回该值是安全的,因为返回default
是安全的。这意味着后面的stackalloc
会失败,因为您将不安全返回的值分配给标记为安全返回的本地span
s;
:返回该值是安全的,因为这是未初始化的局部变量的默认值。这意味着后面的stackalloc
会失败,因为您将不安全返回的值分配给标记为安全返回的本地=stackalloc[0]
方法的真正缺点是它只适用于span
。它不是ref struct
的通用解决方案。但在实践中,这对其他类型的人来说并不是一个大问题。有一些猜测,我们可以如何使它更普遍,但没有足够的证据证明这样做的理由在这一点上。
这与我对“流减少不兼容类型”的回答有关。我不知道为什么我的建议有效,霍尔格正确地敦促我这一点。但即使是他似乎也没有一个明确的解释为什么它能起作用。所以,让我们把它作为自己的问题来问: 以下代码不能在中编译(下面指向ideone的链接是,请参阅http://ideone.com/faq): 这样做是正确的:或者将这个流中的两个谓词结合在一起,就像写: IdeOne演示 尽管事实上我想尝试这一点,但我
问题内容: 来自C语言的Go语言最值得注意的方面之一是,如果在其中声明了一个未使用的变量,编译器将不会编译您的程序。那么,如果在函数中声明了一个未使用的参数,那么为什么要构建此程序呢? 问题答案: 没有正式的原因,但是在golang-nuts上给出的原因是: 未使用的变量始终是编程错误,而编写不使用其所有参数的函数是很常见的。 可以将这些参数保留为未命名(使用_),但这可能会与诸如 func fo
这是CppCon谈话中的一个例子https://www.youtube.com/watch?v=F6Ipn7gCOsY 目标是首先从A打印Hello,然后允许线程B启动。很明显,应该避免繁忙等待,因为它占用大量CPU。 作者说, 循环可以由编译器进行优化(通过将 的值放入寄存器中),因为编译器看到 从不Hibernate,因此永远不会被更改。但是,即使线程从不Hibernate,另一个线程仍然可以
问题内容: 我想知道为什么我们通过编译显示“ Hello,World!”的.c文件得到.o文件。是否大于Java .class文件,该文件也显示“ Hello,World!”? 问题答案: Java使用字节码来独立于平台并进行“预编译”,但是字节码由解释器使用并且被提供为足够紧凑,因此您在已编译的C程序中看到的机器代码并不相同。只需看一下Java编译的完整过程即可: 这是Java程序到机器代码转换
然而,gcc和clang都没有这样优化它--它们都在优化级别生成以下内容: 我的问题是:是因为代码太特殊而不需要优化,还是因为不是对的引用,所以不需要这样的优化?似乎唯一的原因可能是可能有一个非-或-值,但在读取它时没有未定义的行为,但我不确定这是否可能。
我想知道为什么java编译器允许在方法声明中抛出,而方法永远不会抛出异常。因为“throws”是处理异常的一种方式(告诉调用方处理它)。 因为有两种处理异常的方法(抛出和try/catch)。在try/catch中,它不允许捕获try块中未抛出的异常,但它允许在不抛出异常的方法中抛出。