C#デリゲートは不変ですが、なぜそれが重要なのですか?


8

これは、この他の質問のフォローアップ質問です。

バックグラウンド

MSDNデリゲートチュートリアル(C#)から作業すると、次のようになります。

デリゲートが作成されると、関連付けられているメソッドは変更されないことに注意してください。デリゲートオブジェクトは不変です。

そして、コードサンプルでこれを確認します(コード全体を少し広げます)。

public delegate void ProcessBookDelegate(Book book);

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));

質問

ここで明らかに、ここでのコードの記述方法により、プロセスごとに新しいデリゲートが作成されます。私は推測あなたのようなものを行うためにしようと不変性が関連しています

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);
ProcessBookDelegate thisIsATest = new ProcessBookDelegate(totaller.AddBookToTotal);

thisIsATest不変であるときにどれをコンパイルする必要がありますか?では、Microsoftを不変にすることでどのような問題を解決したのでしょうか。C#6がデリゲートを変更可能にした場合、どのような問題が発生しますか?

編集

私は不変性がこれを防ぐと信じています:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);

thisIsATest.ChangeTheDelegateInABadFashion();

someFunction(thisIsATest); //This function works as normal
                           //because the delegate is unchanged
                           //due to immutability

しかし、不変性はこれを防止しません:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);

thisIsATest = new ProcessBookDelegate(ChangeTheDelegateInABadFashion());

someFunction(thisIsATest); //This function works weird now because
                           //it expected the unchanged delegate

この理解は正しいですか?

回答:


15

不変性の意味を混同していると思います。

不変性とは何か

この例を見てみましょう:

string s = "Hello";
s = "World";

あるstringのは不変?はい。
これはコンパイルされますか?はい。
文字列インスタンスを何らかの方法で変更しましたか?いいえ

に変更は加えていません"Hello"。文字列を作成しs、それを変数に割り当て、次に新しい を作成string、変数sをこの新しいで上書きしますstring。不変性はここでは役割を果たしません。

不変性とは

次のようなことを試してみると、

s.MutateInSomeWay();

ここMutateInSomeWayで、は文字列"Hello"(の値s)自体を変更するメソッドです。これは、文字列の不変性のために有効ではありません。

何らかの方法でstringそれを変更するメソッドは、実際にはそれを変更せずstring、変更された値で新しいメソッドを作成し、呼び出し元に返します。

しかし、不変性はこれを防止しません:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);  
thisIsATest = new ProcessBookDelegate(ChangeTheDelegateInABadFashion());
someFunction(thisIsATest); //This function works weird now because
//it expected the unchanged delegate

この理解は正しいですか?

はい、それは完全に有効であり、オブジェクトを変更していないため、不変性はそれを妨げません。もちろん、あなたは何が何であるかを完全に明確にしているわけではないChangeTheDelegateInABadFashion()ので、特定するのは難しいです。


1
不変であるデリゲートの唯一の側面は、呼び出されるメソッドとそのターゲットのIDであることに注意することは価値があります。ターゲットオブジェクト自体は変更可能であることが多く(実際、多くのデリゲートの目的は、ターゲットオブジェクトを変更することです!)、デリゲートがターゲットオブジェクトへの参照のみを保持している場合でもFunc<int>、最初に1を返すことは完全に正当です。呼び出された時間、2秒など-これは本質的に可変オブジェクトのように動作します。
スーパーキャット2014年

このように文字列を変更しようとすると、不変性が発生します。s[2] = 't'
M.kazem Akhgary 2016年

誰かがC#を取り上げているだけなので、ここで@supercatの答えを他の人と直接共有できればいいのにと思います。デリゲートの外部から見える任意の変更が可能な任意のコードを含むことができるという事実を考えると、デリゲートを不変と呼ぶことは私には意味がありません。
trptcolin 2017年

4

要するに理由:

  • 不変オブジェクトは、1つの状態、つまりオブジェクトが作成された状態(セキュリティ)にすることができます
  • スレッドセーフは自由に使用できますか
  • メモリの最適化
  • パフォーマンスが良い

詳細については、Stack Overflowで次の2つの質問を確認してください。


2

標準のスレッドセーフティ デリゲートが決して変更されないことがわかっている場合は、それについていくつかの仮定を行うことができます

特に、悪意のあるコードによって、(偶然であっても)複数の関数に渡されるデリゲートを変更できないことが保証されます。

これにより、バグの追跡が困難になり、プログラマがデリゲートが呼び出されなかった理由が不思議に思われる可能性があります。


デリゲートは変更可能なオブジェクトにバインドできるため、同じデリゲートを繰り返し呼び出しても同じことが行われると期待する理由はありません。ただし、スレッドセーフの点についてはあなたの言うとおりです。デリゲートを不変にする目的は、デリゲートが常に、アタッチされたインスタンスの型と互換性のあるメソッドにバインドされるようにすることです。メソッドとインスタンスが別々に変更される可能性がある場合、悪いことが発生する可能性があります。
スーパーキャット2013

2

簡単な答えは、デリゲートを変更することはほとんど意味がありません。デリゲートは、実行コードのセットである関数への参照です。突然変異は、その関数を突然変異させていることを示唆しています。

自問してみてください。デリゲートのそのメソッドはどのようなもので、何をするのでしょうか。ある意味で、それはその関数のコードを書き直すことになるでしょう。これにより、C#仕様とコンパイラーの実装に膨大な(ほとんど扱いにくい)複雑さが生じ、パフォーマンスコストが大きくてもほとんどメリットがありません。したがって、制限。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.