C# で「=」をオーバーロードできないのはなぜですか? より良い説明を得ることができますか?
12 に答える
メモリ管理言語は通常、オブジェクトではなく参照を操作します。クラスとそのメンバーを定義するときは、オブジェクトの動作を定義していますが、変数を作成するときは、それらのオブジェクトへの参照を操作しています。
これで、演算子 = はオブジェクトではなく参照に適用されます。別の参照を割り当てると、実際には受信側の参照ポイントが他の参照と同じオブジェクトになります。
Type var1 = new Type();
Type var2 = new Type();
var2 = var1;
上記のコードでは、2 つのオブジェクトがヒープ上に作成され、1 つは var1 によって参照され、もう 1 つは var2 によって参照されます。最後のステートメントは、var1 が参照しているのと同じオブジェクトを var2 参照ポイントにします。その行の後、ガベージ コレクターは 2 番目のオブジェクトを解放でき、メモリ内のオブジェクトは 1 つだけになります。プロセス全体で、オブジェクト自体に操作は適用されません。
= をオーバーロードできない理由に戻ると、システムの実装は、参照を使用して実行できる唯一の賢明なことです。オブジェクトに適用される操作をオーバーロードできますが、参照には適用できません。
「=」をオーバーロードすると、作成後にオブジェクト参照を変更できなくなります。...考えてみてください-オーバーロードされたオペレーター内の theObjectWithOverloadedOperator=something への呼び出しは、オーバーロードされたオペレーターへの別の呼び出しになります...では、オーバーロードされたオペレーターは実際に何をしているのでしょうか? たぶん、他のいくつかのプロパティを設定するか、値を新しいオブジェクトに設定しますか(不変性)?通常、「=」が意味するものではありません..
ただし、暗黙的および明示的なキャスト演算子をオーバーライドできます: http://www.blackwasp.co.uk/CSharpConversionOverload.aspx
そんなことをしても意味がないからです。
C# では = はオブジェクト参照を変数に割り当てます。そのため、オブジェクト自体ではなく、変数とオブジェクト参照を操作します。オブジェクトの種類によってはオーバーロードしても意味がありません。
C++ で operator= を定義することは、オブジェクト自体が変数への参照ではなく変数に格納されるため、たとえばスタック上にインスタンスを作成できるクラスに意味があります。したがって、そのような割り当てを実行する方法を定義することは理にかなっています。しかし、C++ でも、通常はポインターまたは参照を介して使用される多態性クラスのセットがある場合、operator= およびコピー コンストラクターをプライベートとして宣言する (または boost::noncopyable から継承する) ことにより、このようにそれらをコピーすることを明示的に禁止します。 C# で = を再定義しない理由とまったく同じです。簡単に言えば、クラス A の参照またはポインタがある場合、それがクラス A のインスタンスを指しているのか、それとも A のサブクラスであるクラス B を指しているのかがよくわかりません。この状況で = を実行する方法を本当に知っていますか?
実際、値セマンティクスを使用してクラスを定義し、これらのクラスのオブジェクトを stack に割り当てるoperator =
ことができれば、オーバーロードは理にかなっています。しかし、C# ではできません。
考えられる説明の 1 つは、代入演算子をオーバーロードすると、適切な参照更新を実行できないことです。人々が参照が更新されることを期待しているときに、 = 演算子がまったく別のことをしている可能性があるため、文字通りセマンティクスを台無しにしてしまいます。あまりプログラマフレンドリーではありません。
暗黙的および明示的な to/from 変換演算子を使用して、割り当てをオーバーロードできないという一見欠点のいくつかを軽減できます。
C# では代入をオーバーロードできます。オブジェクト全体ではなく、そのメンバーのみです。setter を使用してプロパティを宣言します。
class Complex
{
public double Real
{
get { ... }
set { /* do something with value */ }
}
// more members
}
に割り当てるとReal
、独自のコードが実行されます。
オブジェクトへの代入が置き換えられない理由は、それが非常に重要なことを意味するように言語によってすでに定義されているためです。
指摘する特定の単一の理由はないと思います。一般的には、次のような考え方になると思います。
オブジェクトが大きくて複雑なオブジェクトである場合、
=
演算子で代入以外のことを行うと、おそらく誤解を招く可能性があります。オブジェクトが小さなオブジェクトである場合は、それを不変にして、操作を実行するときに新しいコピーを返すこともできます。これにより、代入演算子がすぐに期待どおりに機能するようになります (
System.String
そうです)。
C++ では許可されていますが、注意しないと、多くの混乱とバグハンティングが発生する可能性があります。
この記事では、これについて詳しく説明します。
Because shooting oneself in the foot is frowned upon.
On a more serious note one can only hope you meant comparison rather than assignment. The framework makes elaborate provision for interfering with equality/equivalence evaluation, look for "compar" in help or online with msdn.
このコードは私のために働いています:
public class Class1
{
...
public static implicit operator Class1(Class2 value)
{
Class1 result = new Class1();
result.property = value.prop;
return result;
}
}
オーバーライド割り当てのタイプ
割り当てをオーバーライドするには、次の 2 つのタイプがあります。
- ユーザーが何かを見逃している可能性があると感じ、float から integer への「キャスト」などの「キャスト」をユーザーに強制的に使用させたい場合 、float 値を失う場合
int a = (int)5.4f;
- オブジェクトタイプの変更に気付かずにユーザーにそれをさせたい場合
float f = 5;
割り当てをオーバーライドする方法
1 の場合、explicit
キーワードの使用:
public static explicit override ToType(FromType from){
ToType to = new ToType();
to.FillFrom(from);
return to;
}
2 の場合、implicit
キーワードの使用:
public static implicit override ToType(FromType from){
ToType to = new ToType();
to.FillFrom(from);
return to;
}
アップデート:
注: この実装は、必要に応じてFromType
またはクラスのいずれかで実行できます。制限はなく、クラスの 1 つがすべての変換を保持でき、もう 1 つのクラスはこのためのコードを実装しません。ToType
割り当て操作の特別なセマンティクスを定義できると便利ですが、そのようなセマンティクスが、特定のタイプの 1 つのストレージ ロケーションが別のストレージ ロケーションにコピーされるすべての状況に適用できる場合に限られます。標準 C++ はこのような代入規則を実装していますが、コンパイル時にすべての型を定義する必要があるという贅沢があります。リフレクションとジェネリックがリストに追加されると、事態はさらに複雑になります。
現在、.net の規則では、すべてのバイトをゼロにすることによって、その型が何であるかに関係なく、格納場所をその型の既定値に設定できることが指定されています。さらに、すべてのバイトをコピーすることにより、任意のストレージの場所を同じタイプの別の場所にコピーできることを指定します。これらの規則は、ジェネリックを含むすべての型に適用されます。type の 2 つの変数が与えられるとKeyValuePair<t1,t2>
、システムは、その型のサイズとアラインメントの要件以外は何も知らなくても、1 つの変数を別の変数にコピーできます。t1
、t2
、またはこれらの型のいずれか内の任意のフィールドの型がコピー コンストラクターを実装できる場合、ある構造体インスタンスを別の構造体インスタンスにコピーするコードは、はるかに複雑になる必要があります。
これは、そのような機能がいくつかの重要な利点を提供すると言っているわけではありません。新しいフレームワークが設計されていた場合、カスタムの値割り当て演算子と既定のコンストラクタの利点がコストを上回る可能性があります。ただし、実装のコストは、新しいフレームワークではかなりの額になり、既存のフレームワークでは克服できない可能性があります。