C# CS8176:迭代器不能具有按引用局部变量
在给定的代码中是否存在此错误的真正原因,或者只是在需要跨interator步骤引用的一般用法中可能会出错(在本例中不是这样)C# CS8176:迭代器不能具有按引用局部变量,c#,ref,enumerator,C#,Ref,Enumerator,在给定的代码中是否存在此错误的真正原因,或者只是在需要跨interator步骤引用的一般用法中可能会出错(在本例中不是这样) 编译器想用局部变量作为字段重写迭代器块,以保留状态,不能将ref类型作为字段。是的,你是对的,它不会跨越产生,因此从技术上讲,它可能会被重写以根据需要重新声明,但这使得人类需要记住非常复杂的规则,简单的更改会破坏代码。一条毛毯说“不”要容易得多 此场景中的解决方法(或类似地使用async方法)通常是辅助方法;例如: IEnumerable枚举静态() { (字符串值,in
编译器想用局部变量作为字段重写迭代器块,以保留状态,不能将ref类型作为字段。是的,你是对的,它不会跨越
产生,因此从技术上讲,它可能会被重写以根据需要重新声明,但这使得人类需要记住非常复杂的规则,简单的更改会破坏代码。一条毛毯说“不”要容易得多
此场景中的解决方法(或类似地使用async
方法)通常是辅助方法;例如:
IEnumerable枚举静态()
{
(字符串值,int-next)GetNext(int-index)
{
参考变量p=参考道具[指数];
返回(p.name,p.next);
}
foreach(int i在dict.Values中)
{
(var名称,var next)=GetNext(i);
产生返回名称;
while(下一步>=0)
{
(名称,下一个)=获取下一个(下一个);
产生返回名称;
}
}
}
或
IEnumerable枚举静态()
{
字符串GetNext(ref int next)
{
参考变量p=参考道具[下一步];
next=p.next;
返回p.name;
}
foreach(int i在dict.Values中)
{
var-next=i;
收益返回GetNext(参考next);
while(下一步>=0)
{
收益返回GetNext(参考next);
}
}
}
局部函数不受迭代器块规则的约束,因此可以使用ref局部变量
无法将ref传递给下一个IEnumerator.MoveNext()。但事实并非如此
编译器创建一个状态机类来保存运行时继续下一次迭代所需的数据。就是那个类不能包含ref成员
编译器可以检测到变量只在有限的范围内需要,不需要添加到该状态类中,但正如Marc在回答中所说的,这是一个昂贵的特性,几乎没有额外的好处。记住。所以你可以要求它,但一定要解释它的用途
值得一提的是,Marc的版本比此设置快约4%(根据):
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;
}
}
}
@菲达,这取决于你;我希望它会“按设计”关闭,而且:我支持closure我希望这是一个现在不值得追求的角落案例,但支持关闭吗?好的,我期待积压工作。(附言:我接受,甚至不会尝试)@firda,因为编译器能够证明它的案例数量有限,而且对人类来说很复杂
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)
{
}
}
}
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 |