不纯的方法被称为只读字段
我正在使用Visual Studio 2010 + Resharper,并在以下代码中显示警告:
if (rect.Contains(point)) { ... }
rect
是一个readonly Rectangle
字段,Resharper显示给我这个警告:
“不纯方法”被称为值types的只读字段。
什么是不纯的方法,为什么这个警告显示给我?
首先,乔恩,迈克尔和贾里德的答案基本上是正确的,但是我还有其他一些我想添加的东西。
什么是“不纯”的方法?
表征纯方法更容易。 “纯”的方法有以下特点:
- 其产出完全由其投入决定; 它的输出不依赖于像一天中的时间或硬盘上的位的外部性。 其产出不取决于其历史; 用给定的参数调用方法两次应该给出相同的结果。
- 纯粹的方法在周围的世界中不会产生可观察的突变。 一个纯粹的方法可能会select为了效率而改变私人状态,但是一个纯粹的方法不会改变它的论点的一个领域。
例如, Math.Cos
是一个纯粹的方法。 它的输出只依赖于它的input,input不会被调用改变。
不纯的方法是一种不纯的方法。
通过只读结构去污染方法有什么危险?
有两个想到。 第一个是Jon,Michael和Jared指出的那个,这是Resharper警告你的那个。 当你在一个结构上调用一个方法的时候,我们总是传递一个对接收者variables的引用,以防万一这个方法想要改变这个variables。
那么,如果你在一个值而不是一个variables上调用这种方法呢? 在这种情况下,我们创build一个临时variables,将值复制到该variables中,然后将引用传递给该variables。
readonlyvariables被认为是一个值,因为它不能在构造函数之外进行变异。 所以我们将这个variables拷贝到另一个variables中,当你打算变异variables时,不纯的方法可能会改变拷贝。
这是传递只读结构作为接收器的危险。 传递包含只读字段的结构也是有危险的。 一个包含只读字段的结构是一种常见的做法,但是它本质上是在写一个types系统没有现金的支票。 存储器的所有者确定特定variables的“只读”(read-only)。 一个引用types的实例“拥有”自己的存储,但是一个值types的实例不是!
struct S { private readonly int x; public S(int x) { this.x = x; } public void Badness(ref S s) { Console.WriteLine(this.x); s = new S(this.x + 1); // This should be the same, right? Console.WriteLine(this.x); } }
一个人认为this.x
不会改变,因为x是一个只读字段而Badness
不是一个构造函数。 但…
S s = new S(1); s.Badness(ref s);
…清楚地表明了这种错误。 this
和s
是指同一个variables,而且这个variables不是只读的!
不纯的方法就是不保证保持原来的价值。
在.NET 4中,你可以用[Pure]
来装饰方法和types来声明它们是纯的,R#会注意到这一点。 不幸的是,你不能将它应用到别人的成员,而且就我所知,无法说服R#.NET 3.5项目中的types/成员是纯粹的。 (这一直在野田时代咬我)
这个想法是,如果你调用一个方法来改变一个variables,但是你只需要一个只读字段,那么它可能不是你想做的,所以R#会提醒你这个。 例如:
public struct Nasty { public int value; public void SetValue() { value = 10; } } class Test { static readonly Nasty first; static Nasty second; static void Main() { first.SetValue(); second.SetValue(); Console.WriteLine(first.value); // 0 Console.WriteLine(second.value); // 10 } }
如果每个方法都是纯粹的,那么这将是一个非常有用的警告。 不幸的是,他们不是,所以有很多误报:(
简单的答案是这是一个误报,你可以放心地忽略这个警告。
较长的答案是访问一个只读值types创build它的一个副本 ,所以任何方法所做的值的变化只会影响副本。 ReSharper没有意识到Contains
是一个纯粹的方法(意思是没有副作用)。 Eric Lippert在这里谈到: 突变只读结构
这听起来像Reshaprer认为,方法Contains
可以改变rect
值。 由于rect
是readonly struct
因此C#编译器会对该值进行防御性副本以防止该方法突变readonly
字段。 基本上最终的代码看起来像这样
Rectangle temp = rect; if (temp.Contains(point)) { ... }
Resharper在这里警告你, Contains
可能会以一种会立即丢失的方式进行变异,因为它发生在临时的。
不纯的方法是一种可能有副作用的方法。 在这种情况下,Resharper似乎认为它可以改变rect
。 它可能不是,但证据链被打破。