在给定的代码中是否存在此错误的真正原因,或者只是在一般使用中可能会出错,其中跨交互器步骤需要引用(在这种情况下并非如此)?
IEnumerable<string> EnumerateStatic()
{
foreach (int i in dict.Values)
{
ref var p = ref props[i]; //< CS8176: Iterators cannot have by-reference locals
int next = p.next;
yield return p.name;
while (next >= 0)
{
p = ref props[next];
next = p.next;
yield return p.name;
}
}
}
struct Prop
{
public string name;
public int next;
// some more fields like Func<...> read, Action<..> write, int kind
}
Prop[] props;
Dictionary<string, int> dict;
dict
是名称索引映射,不区分大小写Prop.next
是指向要迭代的下一个节点(-1 作为终止符;因为 dict
不区分大小写,并且添加此链表是为了通过区分大小写的搜索并回退到第一个来解决冲突)。我现在看到两个选项:
name
,第二次用于 next
)。也许编译器会得到它并产生最佳的机器代码。 (我正在为 Unity 创建脚本引擎,这确实对性能至关重要。也许它只检查一次边界并在下次免费使用类似引用/指针的访问。)也许是 3. (2b, 2+1/2) 只需复制结构(x64 上的 32B,三个对象引用和两个整数,但可能会增长,看不到 future )。可能不是很好的解决方案(我要么关心并编写迭代器,要么与 2 一样好。)
我所了解的:
ref var p
以后不能住yield return
,因为编译器正在构建迭代器——一个状态机,ref
不能传递给下一个 IEnumerator.MoveNext()
.但这里的情况并非如此。我不明白的地方:
为什么要强制执行这样的规则,而不是尝试实际生成迭代器/枚举器以查看是否这样
ref var
需要越过边界(这里不需要)。或者任何其他看起来可行的工作方式(我确实理解我想象的更难实现并期望答案是:罗斯林人有更好的事情要做。再次,没有冒犯,完全有效的答案。)预期答案:
如果您想要/需要更多上下文,则适用于此项目:https://github.com/evandisoft/RedOnion/tree/master/RedOnion.ROS/Descriptors/Reflect (Reflected.cs 和 Members.cs)
可重现的示例:
using System.Collections.Generic;
namespace ConsoleApp1
{
class Program
{
class Test
{
struct Prop
{
public string name;
public int next;
}
Prop[] props;
Dictionary<string, int> dict;
public IEnumerable<string> Enumerate()
{
foreach (int i in dict.Values)
{
ref var p = ref props[i]; //< CS8176: Iterators cannot have by-reference locals
int next = p.next;
yield return p.name;
while (next >= 0)
{
p = ref props[next];
next = p.next;
yield return p.name;
}
}
}
}
static void Main(string[] args)
{
}
}
}
最佳答案
the ref cannot be passed to next IEnumerator.MoveNext(). But that is not the case here.
编译器创建一个状态机类来保存运行时所需的数据,以继续下一次迭代。那个类不能包含 ref 成员。
编译器可以检测到变量只在有限的范围内需要,而不需要添加到该状态类中,但正如 Marc 在他们的回答中所说,这是一个昂贵的功能,几乎没有额外的好处。记住,features start at -100 points .所以你可以要求它,但一定要解释它的用途。
对于它的值(value),对于此设置,Marc 的版本要快约 4%(根据 BenchmarkDotNet):
public class StructArrayAccessBenchmark
{
struct Prop
{
public string name;
public int next;
}
private readonly Prop[] _props =
{
new Prop { name = "1-1", next = 1 }, // 0
new Prop { name = "1-2", next = -1 }, // 1
new Prop { name = "2-1", next = 3 }, // 2
new Prop { name = "2-2", next = 4 }, // 3
new Prop { name = "2-2", next = -1 }, // 4
};
readonly Dictionary<string, int> _dict = new Dictionary<string, int>
{
{ "1", 0 },
{ "2", 2 },
};
private readonly Consumer _consumer = new Consumer();
// 95ns
[Benchmark]
public void EnumerateRefLocalFunction() => enumerateRefLocalFunction().Consume(_consumer);
// 98ns
[Benchmark]
public void Enumerate() => enumerate().Consume(_consumer);
public IEnumerable<string> enumerateRefLocalFunction()
{
(string value, int next) GetNext(int index)
{
ref var p = ref _props[index];
return (p.name, p.next);
}
foreach (int i in _dict.Values)
{
var (name, next) = GetNext(i);
yield return name;
while (next >= 0)
{
(name, next) = GetNext(next);
yield return name;
}
}
}
public IEnumerable<string> enumerate()
{
foreach (int i in _dict.Values)
{
var p = _props[i];
int next = p.next;
yield return p.name;
while (next >= 0)
{
p = _props[next];
next = p.next;
yield return p.name;
}
}
}
结果:| Method | Mean | Error | StdDev |
|-------------------------- |----------:|---------:|---------:|
| EnumerateRefLocalFunction | 94.83 ns | 0.138 ns | 0.122 ns |
| Enumerate | 98.00 ns | 0.285 ns | 0.238 ns |
关于c# - CS8176 : Iterators cannot have by-reference locals,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63728889/