对于只读字段调用Impure方法

时间:2012-03-29 14:29:37

标签: c# resharper

我正在使用 Visual Studio 2010 + Resharper ,它会在以下代码中显示警告:

if (rect.Contains(point))
{
    ...
}

rectreadonly Rectangle字段,Resharper向我显示此警告:

  

“只为值类型的只读字段调用Impure方法。”

什么是不纯净的方法,为什么会向我显示此警告?

5 个答案:

答案 0 :(得分:93)

首先,Jon,Michael和Jared的答案基本上是正确的,但我还有一些我想添加的内容。

  

“不纯”方法的含义是什么?

表征纯方法更容易。 “纯”方法具有以下特征:

  • 其输出完全取决于其输入;它的输出不依赖于外部因素,如一天中的时间或硬盘上的位。它的输出并不取决于它的历史;使用给定参数两次调用该方法应该给出相同的结果。
  • 纯方法在其周围的世界中不会产生任何可观察到的突变。为了效率,纯方法可以选择改变私有状态,但是纯方法不会改变其参数的字段。

例如,Math.Cos是纯方法。它的输出仅取决于其输入,并且调用不会改变输入。

不纯的方法是一种不纯粹的方法。

  

将只读结构传递给不纯的方法有哪些危险?

有两个想到的。第一个是Jon,Michael和Jared指出的那个,这是Resharper警告你的那个。当你在一个struct上调用一个方法时,我们总是传递一个对接收者变量的引用,以防该方法希望改变变量。

那么如果你在一个值而不是一个变量上调用这样的方法呢?在这种情况下,我们创建一个临时变量,将值复制到其中,并传递对变量的引用。

readonly变量被认为是一个值,因为它不能在构造函数之外变异。所以我们将变量复制到另一个变量,当你打算改变变量时,不纯的方法可能会改变副本。

将readonly结构作为接收器传递是危险的。传递包含只读字段的结构也存在危险。包含只读字段的结构是一种常见的做法,但它实质上是在检查类型系统没有资金来支付现金;特定变量的“只读”由存储的所有者确定。引用类型的实例“拥有”其自己的存储,但值类型的实例不支持!

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);

...清楚地证明了这种虚假。 thiss引用同一个变量,而 变量不是readonly!

答案 1 :(得分:48)

不纯的方法是不保证保留该值的方法。

在.NET 4中,您可以使用[Pure]来装饰方法和类型,以声明它们是纯粹的,并且R#会注意到这一点。不幸的是,你不能将它应用于其他人的成员,并且你不能说服R#,据我所知,类型/成员在.NET 3.5项目中是纯粹的。 (这一直困扰着我Noda Time。)

想法是,如果你正在调用一个变量变量的方法,但你在一个只读字段上调用它,它可能做你做的事情想要,所以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
    }
}

如果每个实际上纯粹的方法都以这种方式声明,那么这将是一个非常有用的警告。不幸的是他们不是,所以有很多误报:(

答案 2 :(得分:14)

简短的回答是这是误报,你可以放心地忽略警告。

更长的答案是访问只读值类型会创建它的副本,因此方法所做的值的任何更改都只会影响副本。 ReSharper没有意识到Contains是一种纯粹的方法(意味着它没有副作用)。 Eric Lippert在这里谈到它:Mutating Readonly Structs

答案 3 :(得分:11)

听起来Reshaprer认为方法Contains可以改变rect值。由于rectreadonly struct,因此C#编译器会对值进行防御性复制,以防止该方法改变readonly字段。基本上最终的代码看起来像这样

Rectangle temp = rect;
if (temp.Contains(point)) {
  ...
}

Resharper在此警告你,Contains可能会以一种临时丢失的方式改变rect,因为它发生在临时状态。

答案 4 :(得分:5)

Impure方法是一种可能产生副作用的方法。在这种情况下,Resharper似乎认为它可以改变rect。它可能没有,但证据链被打破了。