web-dev-qa-db-ja.com

読み取り専用フィールドに対して不純なメソッドが呼び出されました

私はVisual Studio 2010+Resharperを使用していますが、警告が表示されます次のコード:

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

rectreadonly Rectangleフィールド、およびResharperはこの警告を表示します。

「Impureメソッドは、値型の読み取り専用フィールドに対して呼び出されます。」

不純な方法とは何か、なぜこの警告が表示されるのか?

80
Acidic

まず、Jon、Michael、Jaredの答えは基本的に正しいですが、私はそれらに追加したいことがいくつかあります。

「不純な」方法とはどういう意味ですか?

純粋なメソッドを特徴付けることは簡単です。 「純粋な」メソッドには、次の特性があります。

  • 出力は入力によって完全に決定されます。その出力は、時刻やハードディスク上のビットなどの外部性に依存しません。出力は履歴に依存しません。特定の引数でメソッドを2回呼び出すと、同じ結果が得られます。
  • 純粋な方法では、周囲の世界で目に見える突然変異は生じません。純粋なメソッドは、効率のためにプライベート状態を変更することを選択できますが、純粋なメソッドは、たとえば引数のフィールドを変更しません。

例えば、 Math.Cosは純粋なメソッドです。出力は入力のみに依存し、入力は呼び出しによって変更されません。

不純な方法とは、純粋ではない方法です。

読み取り専用の構造体を渡してメソッドを不純にする危険性は何ですか?

思い浮かぶのは2つあります。 1つ目はJon、Michael、Jaredが指摘したもので、これはResharperがあなたに警告しているものです。構造体でメソッドを呼び出すとき、メソッドが変数を変更したい場合に備えて、常にレシーバーである変数への参照を渡します。

それでは、変数ではなく値でそのようなメソッドを呼び出すとどうなりますか?その場合、一時変数を作成し、その変数に値をコピーして、変数への参照を渡します。

読み取り専用変数は、コンストラクターの外部で変更できないため、値と見なされます。そのため、変数を別の変数にコピーしていますが、不純なメソッドは、変数を変更しようとするときに、コピーを変更している可能性があります。

これは、読み取り専用の構造体をreceiverとして渡す危険です。また、読み取り専用フィールドを含む構造体を渡す危険もあります。読み取り専用フィールドを含む構造体は一般的な方法ですが、本質的には、型システムに現金化する資金がないことをチェックします。特定の変数の「読み取り専用」は、ストレージの所有者によって決定されます。参照型のインスタンスは自身のストレージを「所有」しますが、値型のインスタンスは所有しません!

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は同じ変数を参照し、that変数は読み取り専用ではありません!

95
Eric Lippert

不純なメソッドとは、値をそのままにしておくことが保証されていないメソッドです。

.NET 4では、メソッドと型を [Pure] 純粋であることを宣言します。R#はこれに注意します。残念ながら、それを他の誰かのメンバーに適用することはできません。また、私の知る限り、型/メンバーが.NET 3.5プロジェクトで純粋であることをR#に納得させることはできません。 (これは Noda Time 常に私に噛みつきます。)

ideaは、変数を変更するメソッドを呼び出すが、読み取り専用フィールドで呼び出す場合は、おそらくnotあなたがしたいことをしているので、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
    }
}

実際に純粋なすべてのメソッドがそのように宣言された場合、これは非常に便利な警告になります。残念ながらそうではないので、多くの誤検知があります:(

50
Jon Skeet

簡単な答えは、これは誤検知であり、警告を安全に無視できることです。

より長い答えは、読み取り専用の値型にアクセスすると、そのcopyが作成されるため、メソッドによって行われた値への変更は、コピー。 ReSharperはContainsが純粋なメソッドであることを認識しません(つまり、副作用がないことを意味します)。 Eric Lippertがここでそれについて語っています: Mutating Readonly Structs

15
Michael Liu

Reshaprerは、メソッドContainsrectの値を変更できると信じているようです。 rectreadonly struct C#コンパイラは、メソッドがreadonlyフィールドを変更しないように値の防御コピーを作成します。基本的に最終コードは次のようになります

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

Resharperは、Containsrectを変更する可能性があることを警告しています。

11
JaredPar

不純メソッドとは、副作用が生じる可能性のあるメソッドです。この場合、Resharperはrectを変更できると考えているようです。おそらくそうではありませんが、一連の証拠が壊れています。

5
Henk Holterman