私が使用しているのVisual Studio 2010 + ReSharperのを、それが次のコードに警告を示しています。
if (rect.Contains(point))
{
...
}
rect
はreadonly Rectangle
フィールドであり、Resharperは次の警告を表示します。
「値型の読み取り専用フィールドに対してImpureメソッドが呼び出されます。」
不純な方法とは何ですか?なぜこの警告が表示されるのですか?
回答:
まず、Jon、Michael、Jaredの答えは基本的に正しいですが、さらにいくつか追加したいことがあります。
「不純な」方法とはどういう意味ですか?
純粋なメソッドを特徴づける方が簡単です。「純粋」メソッドには、次の特徴があります。
たとえば、Math.Cos
は純粋な方法です。その出力はその入力のみに依存し、入力は呼び出しによって変更されません。
不純な方法は、純粋ではない方法です。
読み取り専用構造体を渡して不純なメソッドにすることの危険性にはどのようなものがありますか?
頭に浮かぶのは2つあります。1つ目は、Jon、Michael、Jaredが指摘したもので、これはResharperが警告しているものです。構造体でメソッドを呼び出すと、メソッドが変数を変更したい場合に備えて、レシーバーである変数への参照を常に渡します。
では、変数ではなく値に対してそのようなメソッドを呼び出すとどうなるでしょうか。その場合、一時変数を作成し、その値をコピーして、変数への参照を渡します。
読み取り専用変数は、コンストラクターの外部で変更できないため、値と見なされます。そのため、変数を別の変数にコピーしています。変数を変更する場合は、不純なメソッドがコピーを変更している可能性があります。
これは、読み取り専用構造体をレシーバーとして渡すことの危険性です。読み取り専用フィールドを含む構造体を渡す危険もあります。読み取り専用フィールドを含む構造体は一般的な方法ですが、基本的には、型システムに現金化する資金がないことを確認するためのチェックを記述しています。特定の変数の「読み取り専用性」は、ストレージの所有者によって決定されます。参照型のインスタンスはそれ自体のストレージを「所有」しますが、値型のインスタンスは所有しません。
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
参照し、その変数は読み取り専用ではありません!
struct Id {
private readonly int _id;
public Id(int id) { _id = id; }
public int ToInt() => _id;
}
はなぜですか?
return
ます。それに基づいて、唯一の基準はメソッドが[Pure]
属性を持っているかどうかだと思います。
rect
ます。のコピーがメソッドにrect
渡されると言っているのContains
ですか?
不純な方法とは、値をそのままにしておくことが保証されていない方法です。
.NET 4では、メソッドと型をで装飾[Pure]
して、それらが純粋であることを宣言できます。R#はこれに注意します。残念ながら、それを他の誰かのメンバーに適用することはできません。また、私が知る限り、.NET3.5プロジェクトでタイプ/メンバーが純粋であることをR#に納得させることはできません。(これはいつも野田時間で私を噛みます。)
アイデアは、あなたがメソッド変異する変数呼んでいるが、あなたは読み取り専用フィールドでそれを呼び出す場合、おそらくということですされていない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
}
}
実際に純粋なすべてのメソッドがそのように宣言されている場合、これは非常に便利な警告になります。残念ながらそうではないので、誤検知がたくさんあります:(
JetBrains.Annotations.PureAttribute
代わりに使用できますSystem.Diagnostics.Contracts.PureAttribute
。これらはReSharperコード分析で同じ意味を持ち、.NET 3.5、.NET 4、またはSilverlightで同等に機能するはずです。XMLファイルを使用して所有していないアセンブリに外部から注釈を付けることもできます(ReSharperビンパスのExternalAnnotationsディレクトリを参照してください)。これは非常に便利です。
System.Diagnostics.Contracts.PureAttribute
、R#8.2ではこの警告が抑制されなかったことがわかりましJetBrains.Annotations.PureAttribute
た。2つの属性の説明も異なりPure
ます。contracts属性は「結果はパラメーターのみに依存する」ことを意味し、JetBrainsPure
は結果の計算に使用されるオブジェクトの状態を除外せずに「目に見える状態変化を引き起こさない」ことを意味します。(しかし、それでもPure
この警告に同じ影響を与えない契約はおそらくバグです。)
簡単に言うと、これは誤検知であり、警告は無視しても問題ありません。
より長い答えは、読み取り専用の値型にアクセスするとそのコピーが作成されるため、メソッドによって行われた値への変更はコピーにのみ影響するということです。ReSharperContains
は、それが純粋な方法であることを認識していません(つまり、副作用がありません)。Eric Lippertがここでそれについて話します:読み取り専用構造体の変更
private readonly SpinLock _spinLock = new SpinLock();
。-このようなロックは完全に役に立たないでしょう(読み取り専用修飾子により、Enterメソッドが呼び出されるたびにオンザフライコピーが作成されるため)
Reshaprerは、メソッドContains
がrect
値を変更できると信じているようです。のでrect
あるreadonly struct
C#コンパイラは、値の守備のコピーが変異からメソッドを防止することができるreadonly
フィールドを。基本的に、最終的なコードは次のようになります
Rectangle temp = rect;
if (temp.Contains(point)) {
...
}
Resharperはここで警告していますが、一時的に発生したためにすぐに失われるContains
可能性がありますrect
。