C#拡張メソッドによる演算子のオーバーロード


174

拡張メソッドを使用して、C#StringBuilderクラスに演算子オーバーロードを追加しようとしています。具体的には、を指定するとStringBuilder sbsb += "text"と同等になりたいと思いsb.Append("text")ます。

の拡張メソッドを作成するための構文は次のとおりですStringBuilder

public static class sbExtensions
{
    public static StringBuilder blah(this StringBuilder sb)
    {
        return sb;
    }
} 

blah拡張メソッドがに正常に追加されますStringBuilder

残念ながら、演算子のオーバーロードは機能していないようです:

public static class sbExtensions
{
    public static StringBuilder operator +(this StringBuilder sb, string s)
    {
        return sb.Append(s);
    }
} 

他の問題の中で、キーワードthisはこのコンテキストでは許可されていません。

拡張メソッドを介して演算子のオーバーロードを追加することは可能ですか?もしそうなら、それについて行く適切な方法は何ですか?


4
これは最初はクールなアイデアのように思えますが、var otherSb = sb + "hi"を検討してください。
手斧-19:

回答:


150

拡張メソッドは静的クラスになければならず、静的クラスは演算子のオーバーロードを持つことができないため、これは現在可能ではありません。

Mads Torgersen、C#言語のPMによると:

... Orcasリリースでは、拡張プロパティ、イベント、演算子、静的メソッドなどとは対照的に、慎重なアプローチを取り、通常の拡張メソッドのみを追加することにしました。通常の拡張メソッドはLINQに必要なものであり、他のメンバーの種類によっては簡単に模倣できない構文的に最小限のデザイン。

他の種類のエクステンションメンバーが有用である可能性があることをますます認識しているので、Orcasの後でこの問題に戻ります。ただし、保証はありません。

編集:

気がついたのですが、マッドは 同じ記事の

次のリリースではこれを行わないことを報告して申し訳ありません。私達は私達の計画でエクステンションメンバーを真剣に受け止め、それらを正しくするために多くの努力を費やしましたが、結局それを十分にスムーズにすることができず、他の興味深い機能に道を譲ることに決めました。

これは、今後のリリースに向けてまだ検討中です。適切な設計を推進するのに役立つ説得力のあるシナリオが大量に得られた場合に役立ちます。


この機能は現在C#8.0の(潜在的に)テーブルにあります。Madsはそれをここで実装することについてもう少し話します


このページはその後削除されました。この問題はまだ解決されていません。
Chris Moschini

17
残念な。TimeSpanにスカラー値を乗算する演算子を追加したかっただけです... :(
Filip Skakun

これと同じ概念を実装StringしてPowerShell にキャストしたいと思っていましたScriptBlock
Trevor Sullivan、

つまり、%をオーバーロードしてブール値をxorにすることはできませんか?:(で死んでtrue.Xor(false)、その後
スパーク

3
@SparK ^はC#のxor演算子です
Jacob Krall

57

この「拡張演算子」を使用する場所を制御する場合(通常、拡張メソッドを使用して行います)、次のようなことができます。

class Program {

  static void Main(string[] args) {
    StringBuilder sb = new StringBuilder();
    ReceiveImportantMessage(sb);
    Console.WriteLine(sb.ToString());
  }

  // the important thing is to use StringBuilderWrapper!
  private static void ReceiveImportantMessage(StringBuilderWrapper sb) {
    sb += "Hello World!";
  }

}

public class StringBuilderWrapper {

  public StringBuilderWrapper(StringBuilder sb) { StringBuilder = sb; }
  public StringBuilder StringBuilder { get; private set; }

  public static implicit operator StringBuilderWrapper(StringBuilder sb) {
    return new StringBuilderWrapper(sb);
  }

  public static StringBuilderWrapper operator +(StringBuilderWrapper sbw, string s) { 
      sbw.StringBuilder.Append(s);
      return sbw;
  }

} 

StringBuilderWrapperクラスが宣言暗黙変換演算子から、StringBuilder 及び所望の宣言+演算子。このようにして、a StringBuilderをに渡すことができますReceiveImportantMessage。これは暗黙的にに変換されStringBuilderWrapper、そこで+演算子を使用できます。

この事実を呼び出し元に対してより透過的にするにはReceiveImportantMessage、をとることを宣言し、次のStringBuilderようなコードを使用します。

  private static void ReceiveImportantMessage(StringBuilder sb) {
    StringBuilderWrapper sbw = sb;
    sbw += "Hello World!";
  }

または、すでにを使用している場合にインラインで使用するにはStringBuilder、次のように実行します。

 StringBuilder sb = new StringBuilder();
 StringBuilderWrapper sbw = sb;
 sbw += "Hello World!";
 Console.WriteLine(sb.ToString());

私は同様のアプローチを使用してより理解しやすくすることについて投稿を作成しましIComparable


2
@レオン:私は本当にそれを継承するのではなく、作成するつもりでした。とにかく、封印されているので継承できませんでした。
ジョルダン2010

5
@レオン:それがこのテクニックの中心です。それを可能にする暗黙の変換演算子が宣言されStringBuilderWrapperているので、私はそれを行うことができます。
ジョルダン2010

1
@pylover:そのとおりです。これには、新しい型を作成する必要があります。これにより、StringBuilder型がラップされ、そこから暗黙的な変換演算子が提供されます。その後、例に示すように、文字列リテラルで使用できます。sb += "Hello World!";
JordãoJul

2
次に、拡張メソッドをString:に追加することをお勧めしますPushIndent(" ".X(4))(と呼ぶこともできますTimes)。または、おそらくこのコンストラクタを使用しますPushIndent(new String(' ', 4))
ジョルダン

1
@Jordão:すばらしい答え;)
Vinicius

8

これは現在不可能であるようです-Microsoft Connectでこの機能を要求するオープンフィードバックの問題があります:

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=168224

将来のリリースで表示される可能性があるが、現在のバージョンでは実装されていないことを示唆しています。


「現時点では不可能」とはどういう意味ですか?F#は拡張機能をすべてサポートしているため、CLRで可能でなければなりません。
Matthew Olenik

1
CLRではなくC#では不可能だと彼は言っていると思います。とにかく、拡張メソッド全体はC#コンパイラのトリックです。
tofi9 2010年

1
リンクは現在死んでいます。
CBHacking 2017

1

演算子を実行することは不可能ですが、常にAdd(またはConcat)、Subtract、およびCompareメソッドを作成することもできます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;    

namespace Whatever.Test
{
    public static class Extensions
    {
        public static int Compare(this MyObject t1, MyObject t2)
        {
            if(t1.SomeValueField < t2.SomeValueField )
                return -1;
            else if (t1.SomeValueField > t2.SomeValueField )
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }

        public static MyObject Add(this MyObject t1, MyObject t2)
        {
            var newObject = new MyObject();
            //do something  
            return newObject;

        }

        public static MyObject Subtract(this MyObject t1, MyObject t2)
        {
            var newObject= new MyObject();
            //do something
            return newObject;    
        }
    }


}

1

はぁ!私はsb + =(thing)に対して、まったく同じ欲求で「拡張演算子のオーバーロード」を探していました。

ここで答えを読んだ後(答えが「いいえ」であることがわかりました)、私の特定のニーズのために、sb.AppendLineとsb.AppendFormatを組み合わせた拡張メソッドを使用しました。

public static class SomeExtensions
{
    public static void Line(this StringBuilder sb, string format, params object[] args)
    {
        string s = String.Format(format + "\n", args);
        sb.Append(s);
    }

}

など、

sb.Line("the first thing is {0}",first);
sb.Line("the second thing is {0}", second);

一般的な答えではありませんが、この種のことを検討している将来の求職者にとっては興味深いかもしれません。


4
拡張メソッドは、のAppendLine代わりに名前を付けた方が読みやすくなると思いますLine
DavidRR 2015

0

ラッパーと拡張機能を使用してリギングすることは可能ですが、適切に実行することは不可能です。あなたは完全に目的を打ち負かすゴミで終わります。私はそれをここにどこかで投稿していますが、その価値はありません。

ところで、すべての数値変換は、修正する必要のある文字列ビルダーでガベージを作成します。機能するラッパーを作成する必要があり、それを使用しています。それは一見の価値があります。

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