web-dev-qa-db-ja.com

C#で代入演算子をオーバーロードするための回避策はありますか?

C++とは異なり、C#では代入演算子をオーバーロードできません。

非常に大きな数の算術演算用にカスタムNumberクラスを実行していて、int、decimalなどの組み込み数値型のルックアンドフィールを持たせたいです。算術演算子をオーバーロードしましたが、割り当ては残ります...

次に例を示します。

Number a = new Number(55); 
Number b = a; //I want to copy the value, not the reference

その問題の回避策はありますか?

32
Boyan Nikolov

あなたが本当にこれを必要としているのかどうかはまだ私にはまったくわかりません。どちらか:

  • 数値型は構造体である必要があります(おそらく、数値は構造体の最も一般的な例です)。タイプを動作させたいすべてのタイプ(int、decimalなど)は構造体であることに注意してください。

または:

  • Numberタイプは不変である必要があり、すべてのミューテーション操作で新しいインスタンスが返されます。この場合、割り当て時にデータをコピーする必要はありません。 (実際、構造体であるかどうかに関係なく、型は不変である必要があります。可変構造体は悪であり、数値は確かに可変参照型であってはなりません。)
28
Jon Skeet

'implicit'キーワードを使用して、割り当てのオーバーロードを作成できます。

Fooのようなタイプがあり、文字列から暗黙的に変換可能であると感じているとします。 Fooクラスに次の静的メソッドを記述します。

public static implicit operator Foo(string normalString)
{
    //write your code here to go from string to Foo and return the new Foo.
}

それが済んだら、コードで次を使用できます。

Foo x = "whatever";
44

A = bであるため、C++の外観で回避することはできません。 C#以外のC++のセマンティクスがあります。 C#では、a = b; bのような同じオブジェクトを指し示します。 C++では、a = bはaの内容を変更します。どちらにも浮き沈みがあります。それはあなたがするようなものです

MyType * a = new MyType();
MyType * b = new MyType(); 
a = b; /* only exchange pointers. will not change any content */

C++の場合(最初のオブジェクトへの参照が失われ、メモリリークが発生します。ただし、ここでは無視しましょう)。そのためにも、C++で割り当て演算子をオーバーロードすることはできません。

回避策は簡単です。

MyType a = new MyType();
MyType b = new MyType();

// instead of a = b
a.Assign(b);

免責事項:私はC#開発者ではありません

このような書き込み専用プロパティを作成できます。次に、a.Self = b;を実行します。上記。

public MyType Self {
    set {
        /* copy content of value to this */
        this.Assign(value);
    }
}

さて、これはnot良いです。驚き最小の原則(POLS)に違反しているため。 a.Self = b;を実行した場合、aが変更されることは期待できません。

参照を渡すときにデータのコピーを作成する代わりに、クラスを不変にすることができます。クラスが不変である場合、複数の参照がある場合は変更できないため、問題はありません。

もちろん、データを変更する操作は新しいインスタンスを返します。

3

以前の投稿はこれを示唆しました:

パブリック静的暗黙演算子Foo(string normalString){}

私はこのアプローチを試しました...しかし、それを機能させるには、これが必要です:

public staticimplicit operator Foo(Foo original){}

また、コンパイラーでは、正確な型からも、自分の基本型からも、暗黙の変換関数を使用できません。これは、C#が許可したくない代入演算子をオーバーライドするバックドアの方法であるため、理にかなっています。

2
DMC

これが私自身のために働いた解決策です:

public class MyTestClass
{
   private int a;
   private string str;

   public MyTestClass()
   {
      a = 0;
      str = null;
   }

   public MyTestClass(int a, string str)
   {
      this.a = a;
      this.str = str;
   }

   public MyTestClass Clone
   {
      get
      {
         return new MyTestClass(this.a, this.str);
      }
   }
}

コードのどこか:

MyTestClass test1 = new MyTestClass(5, "Cat");
MyTestClass test2 = test1.Clone;
0
ttrixas