[英]How is direct use of return value different from local variable
Memory<T>
被介绍为“ Span<T>
的包装器,因此您可以在异步/迭代器方法中使用Span
”。 我理解为什么不能在异步中使用Span
,但是ref struct
类型让我认为我从来没有正确理解参数和返回值的概念。 说“使用Memory.Span
创建Span
作为异步方法中的参数,因为您不能在异步方法中使用Span
”对我来说听起来有悖常理。 代码中解释:
async Task<ReadOnlyMemory<char>> GetMemory(string text) => text.AsMemory();
async Task foo(string text)
{
var memory = await GetMemory(text);
// this is valid code
ConsumeSpan(memory.Span);
// this is invalid code
var textSpan = memory.Span;
ConsumeSpan(textSpan);
}
有效代码的行为与无效代码相同。 想知道Memory.Span
可以在 CLR 中实现一些特殊的行为,我试过这个:
Span<char> CreateSpan() => new Span<char>();
async Task bar(string text) {
// both statements are valid
CreateSpan();
_ = CreateSpan();
}
这证伪了我的猜想,让我意识到我可能对生活在方法中的价值观有一个错误的心理模型。 C# 语言中关于有名字和没有名字的值有什么不同吗? 可能回答我自己的问题,带名称的值是变量的定义,因此变量和“未命名值”之间必须存在一些差异。 当我直接将 Span 用作函数参数时,是否会在我的 foo 方法的堆栈上创建 Span 的本地副本,或者这是语言未指定的内容并将其留给优化器决定? 您对日常编程中的未命名值有不同的看法吗? 查看 ref 返回也让我打破了我对未命名变量的假设:
int x = 0;
ref int GetRef() => ref x;
void foobar()
{
GetRef() = 42;
}
您所说的“命名变量”与其他变量之间存在一个主要区别。 在发布模式下,两者的行为相同。 但是在调试模式下,命名变量的生命周期会延长到作用域结束,以便调试器可以随时检查它们。
基本上,请考虑以下代码:
async Task bar(string text)
{
var a = GetValue();
await Task.Yield();
}
如果你把一个断点右括号}
,如果在调试模式下运行时,您将能够检查的值a
。 在发布模式下,该值将被优化掉。
这就是为什么在异步方法中编写var a = CreateSpan()
是非法的。 在发布模式下的某些条件下它可能是安全的,但它会导致变量的范围在调试模式下以不安全的方式扩展。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.