web-dev-qa-db-ja.com

意味のある演算子のオーバーロードの例

C#を学習しているときに、C#が演算子のオーバーロードをサポートしていることを発見しました。私は良い例に問題があります:

  1. 意味をなす(例:羊と牛という名前のクラスを追加する)
  2. 2つの文字列を連結した例ではありません

基本クラスライブラリの例を歓迎します。

12

適切な演算子のオーバーロードの明白な例は、数値が動作するのと同じように動作するクラスです。したがって、BigIntクラス(- Jalayn が示唆)、複素数または matrixクラス( Superbest が示唆する)はすべて、通常の数値と同じ演算を行うため、数学演算子に非常によく対応し、time操作( svick で推奨)は、これらの操作のサブセットに適切にマッピングされます。

少し抽象的には、set like操作を実行するときに演算子を使用できるため、_operator+_は nion 、_operator-_は complement などになります。特に、期待どおりに commutative でない演算に加算または乗算演算子を使用する場合は、これによってパラダイムが広がり始めます。それらがある。

C#自体には、非数値演算子のオーバーロードの優れた例があります。 _+=_と_-=_を使用して delegates を加算および減算します。つまり、それらを登録および登録解除します。 _+=_および_-=_演算子は期待どおりに機能するため、これは適切に機能し、結果としてコードがより簡潔になります。

純粋主義者にとって、文字列_+_演算子の問題の1つは、可換ではないということです。 _"a"+"b"_は_"b"+"a"_と同じではありません。これは一般的であるため、文字列のexceptionを理解していますが、他の型で_operator+_を使用すると可換かどうかはどうすればわかりますか?オブジェクトがstring-likeでない限り、ほとんどの人はそうであると想定しますが、人々が何を想定するかは本当にわかりません。

文字列と同様に、行列の可能性もかなりよく知られています。 Matrix operator* (double, Matrix)がスカラー乗算であることは明らかですが、たとえばMatrix operator* (Matrix, Matrix)行列乗算 (つまり、ドット積乗算の行列)になります。

同様に、デリゲートでの演算子の使用は、明らかに数学からかけ離れているため、これらの間違いを犯すことはほとんどありません。

ちなみに、 2011 ACCUカンファレンスRoger OrrSteve Love でセッションを発表しました いくつかのオブジェクトは他のものより同等です-平等、価値、アイデンティティの多くの意味を見てください 。彼らの スライドはダウンロード可能 であり、Richard Harrisの 浮動小数点の等価性に関する付録 も同様です。要約:_operator==_には十分注意してください

演算子のオーバーロードは非常に強力なセマンティックテクニックですが、使いすぎやすいです。理想的には、オーバーロードされた演算子の影響がコンテキストから非常に明確である状況でのみ使用する必要があります。多くの点で、a.union(b)は_a+b_よりも明確であり、_a*b_はa.cartesianProduct(b)、特にデカルト積の結果は_SetLike<Tuple<T,T>>_ではなく_SetLike<T>_になるためです。

オペレーターのオーバーロードに関する実際の問題は、プログラマーがクラスがある方法で動作することを想定しているが、実際には別の方法で動作する場合に発生します。この種のセマンティッククラッシュは、回避しようとすることが重要であると私が提案しているものです。

27
Mark Booth

BCLでより興味深いケースの1つであるDateTimeTimeSpanについて誰も言及しなかったことに驚いています。あなたはできる:

  • 2つのTimeSpansを加算または減算して、別のTimeSpanを取得します
  • 否定されたTimeSpanを取得するには、TimeSpanに単項マイナスを使用します
  • 2つのDateTimesを減算してTimeSpanを取得します
  • TimeSpanからDateTimeを加算または減算して、別のDateTimeを取得します

多くの型で意味のある演算子の別のセットは、<><=>=です。たとえばBCLでは、Versionがそれらを実装します。

26
svick

頭に浮かぶ最初の例は、 BigInteger の実装です。これにより、大きな符号付き整数を扱うことができます。 MSDNリンクをチェックして、オーバーロードされているオペレーターの数を確認してください(つまり、大きなリストがあり、すべてのオペレーターがオーバーロードされているかどうかはチェックしませんでしたが、確かにそうです)。

また、JavaとJavaは演算子のオーバーロードを許可しないため、次のように書くのが非常に簡単です。

BigInteger bi = new BigInteger(0);
bi += 10;

より、Javaでは:

BigDecimal bd = new BigDecimal(0);
bd = bd.add(new BigDecimal(10));
7
Jalayn

Irony をいじくり回していて、演算子のオーバーロードが非常に使用されているので、これを見てうれしいです。 これはサンプルです 何ができるか.

つまり、Ironyは「.NET言語実装キット」であり、パーサージェネレーター(LALRパーサーを生成)です。 yacc/Lexなどのパーサージェネレーターのような新しい構文/言語を学習する必要がない代わりに、演算子オーバーロードを使用してC#で文法を記述します。これが単純な BNF文法 です。

// BNF 
Expr := Term | BinExpr
Term := number | ParExpr
ParExpr := "(" + Expr + ")"
BinExpr := number + BinOp + number
BinOp := "+" | "-" | "*" | "/"
number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

したがって、これは単純な小さな文法です(私がBNFを学習して文法を構築しているだけなので、矛盾がある場合は失礼します)。 C#を見てみましょう。

  var Expr = new NonTerminal("Expr");
  var Term = new NonTerminal("Term");
  var BinExpr = new NonTerminal("BinExpr");
  var ParExpr = new NonTerminal("ParExpr");
  var BinOp = new NonTerminal("BinOp");
  var Statement = new NonTerminal("Statement");
  var ProgramLine = new NonTerminal("ProgramLine");
  var Program = new NonTerminal("Program", typeof(StatementListNode));
  // BNF Rules - Overloading
  Expr.Rule = Term | BinExpr;
  Term.Rule = number | ParExpr;
  ParExpr.Rule = "(" + Expr + ")";
  BinExpr.Rule = Expr + BinOp + Expr;
  BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**";

ご覧のとおり、演算子のオーバーロードにより、C#での文法の記述はBNFでの文法の記述とほぼ同じです。私にとっては、これは理にかなっているだけでなく、演算子のオーバーロードの優れた使用法です。

5
Jetti

主な例は、operator ==/operator!=です。

参照ではなくデータ値で2つのオブジェクトを簡単に比較する場合は、.Equals(および.GetHashCode!)をオーバーロードし、一貫性を保つために!=および==演算子も実行することができます。

C#で他の演算子のワイルドオーバーロードを見たことはありません(ただし、これが役立つかもしれないEdgeのケースがあると思います)。

3
Ed James

このMSDNの例 は、複素数を実装し、通常の+演算子を使用する方法を示しています。

別の例 は、行列を追加する方法を示し、車をガレージに追加するためにそれを使用しない方法を説明します(リンクを参照)。

1
Superbest

オーバーロードがうまく使用されることはまれですが、実際には起こります。

operator ==とoperator!=のオーバーロードは、2つの考え方を示しています。言い方が簡単だと言うことと、言い方が違うとアドレスの比較ができない(つまり、同じもののコピーだけでなく、メモリ内のまったく同じ場所を指しているのか)オブジェクト)。

特定の状況ではキャスト演算子のオーバーロードが便利だと思います。たとえば、0または1として表されるブール値をXMLでシリアル化/逆シリアル化する必要がありました。ブール()からintへの正しい(暗黙的または明示的な、忘れた)キャストオペレーターがトリックを行いました。

0
MPelletier

これらは、演算子のオーバーロードの場合に一般的に考えられるもののカテゴリには含まれませんが、オーバーロードできる最も重要な演算子の1つは 変換演算子 だと思います。

変換演算子は、数値型に「脱糖」したり、一部のコンテキストで数値型のように動作したりできる値型に特に役立ちます。たとえば、特定の識別子を表す特別なIdタイプを定義し、intを使用すると、Idを受け取るメソッドにintを渡すことができますが、explict変換intからIdまでintを、最初にキャストせずにIdを受け取るメソッドに渡すことはできません。

C#以外の例として、Python言語には、オーバーロード可能な演算子として実装される多くの特別な動作が含まれています。これらには、メンバーシップテスト用のin演算子、()関数のようにオブジェクトを呼び出すための演算子と、オブジェクトの長さまたはサイズを決定するためのlen演算子。

そして、Haskell、Scala、およびその他の多くの関数型言語のような言語があり、+のような名前は通常の関数であり、演算子ではありません(また、中置位置で関数を使用するための言語サポートがあります)。

0
Daniel Pryden

System.Drawing名前空間のPoint Structは、オーバーロードを使用して、演算子のオーバーロードを使用して2つの異なる場所を比較します。

 Point locationA = new Point( 50, 50 );
 Point locationB = new Point( 50, 50 );

 if ( locationA == locationB )
    Console.WriteLine( "Their locations are the same" );
 else
    Console.WriteLine( "Their locations  are different" );

ご覧のとおり、オーバーロードを使用して2つの場所のX座標とY座標を比較する方がはるかに簡単です。

0

数学的なベクトルに精通している場合は、_+_演算子のオーバーロードでの使用が見られるかもしれません。 _a=[1,3]_を使用してベクトル_b=[2,-1]_を追加し、_c=[3,2]_を取得できます。

等号(==)のオーバーロードも役立ちます(おそらくequals()メソッドを実装する方が適切です)。ベクターの例を続けるには:

_v1=[1,3]
v2=[1,3]
v1==v2 // True
_
0
MartinHaTh