为什么结构中的迭代器可以修改它?
我发现允许在值类型中使用迭代器方法来修改this
。
但是,由于CLR的限制,调用方法没有看到修改。 ( this
是通过值传递的)
因此,迭代器和非迭代器中的相同代码会产生不同的结果:
static void Main() { Mutable m1 = new Mutable(); m1.MutateWrong().ToArray(); //Force the iterator to execute Console.WriteLine("After MutateWrong(): " + m1.Value); Console.WriteLine(); Mutable m2 = new Mutable(); m2.MutateRight(); Console.WriteLine("After MutateRight(): " + m2.Value); } struct Mutable { public int Value; public IEnumerable MutateWrong() { Value = 7; Console.WriteLine("Inside MutateWrong(): " + Value); yield break; } public IEnumerable MutateRight() { Value = 7; Console.WriteLine("Inside MutateRight(): " + Value); return new int[0]; } }
输出:
在MutateWrong()里面:7 在MutateWrong()之后:0 在MutateRight()里面:7 在MutateRight()之后:7
为什么在迭代器中改变结构是不是编译器错误(或至少是警告)?
这种行为是一个微妙的陷阱,不容易理解。
匿名方法具有相同的限制, 根本无法使用this
。
注意: 可变结构是邪恶的 ; 这应该永远不会出现在实践中。
为了certificate警告的合理性,应该是程序员可能会得到意外结果的情况。 根据Eric Lippert的说法,“ 我们试图仅在那些我们几乎可以肯定地说代码被破坏,误导或无用的情况下保留警告。 ”这是一个警告会产生误导的实例。
让我们说你有这个完全有效 – 如果不是非常有用 – 对象:
struct Number { int value; public Number(int value) { this.value = value; } public int Value { get { return value; } } // iterator that mutates "this" public IEnumerable UpTo(int max) { for (; value <= max; value++) yield return value; } }
你有这个循环:
var num = new Number(1); foreach (var x in num.UpTo(4)) Console.WriteLine(num.Value);
你期望这个循环打印1,1,1,1
,而不是1,2,3,4
,对吗? 所以这个课程的工作方式完全符合您的预期 这是警告不合理的情况。
由于这显然不是代码被破坏,误导或无用的情况,您如何建议编译器生成错误或警告?
引用自己“可变结构是邪恶的”:)如果你为结构实现扩展方法,你会遇到同样的事情。 如果您尝试在扩展方法中修改结构,您仍将保持原始结构不变。 由于扩展方法签名如下所示,它有点不太令人惊讶:
static void DoSideEffects(this MyStruct x) { x.foo = ...
看着它我们意识到像参数传递之类的东西发生了,因此结构被复制了。 但是当你使用扩展时它看起来像:
x.DoSideEffects()
你会惊讶地发现你的变量x没有任何影响。 我认为在幕后你的yield构造做了类似于扩展的事情。 我会更难说出起始句:“结构是邪恶的”..一般来说;)
我对加布所说的有类似的想法。 至少在理论上似乎可以选择使用struct
来表现类似方法,将该方法的局部变量封装为实例字段:
struct Evens { int _current; public IEnumerable Go() { while (true) { yield return _current; _current += 2; } } }
我的意思是,显然有点奇怪。 这让我想起过去遇到的一些想法,开发人员在这种方法中调制了一些奇怪的方法来调用方法,例如将方法的参数包装到一个对象中,然后让该对象调用方法向后移动,感觉。 我会说这大致是这个。
我并不是说这是明智之举 ,但它至少是一种以你所描述的方式使用this
的方式,可能是有意的,并且在技术上会表现出正确的行为。
虽然我认为.net缺乏不需要修改’this’的方法的特殊属性,但是不清楚应该发生什么。 这样的属性可以用于不可变的类类型以及可变的结构。 使用此类属性标记的方法应仅可用于结构变量,字段和参数,而不能用于临时值。
我认为没有办法避免让迭代器按值捕获struct。 完全有可能在使用迭代器时,它所基于的原始结构可能不再存在。 另一方面,如果struct实现了一个inheritanceIEnumerable
上述就是C#学习教程:为什么结构中的迭代器可以修改它?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/957124.html