web-dev-qa-db-ja.com

C#拡張メソッドを使用した演算子のオーバーロード

拡張メソッドを使用して、C#StringBuilderクラスに演算子オーバーロードを追加しようとしています。具体的には、StringBuildersbを指定すると、_sb += "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はこのコンテキストでは許可されていません。

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

165
Jude Allred

拡張メソッドは静的クラスに存在する必要があり、静的クラスには演算子のオーバーロードを含めることができないため、これは現在不可能です。

Mads Torgersen、C#言語PMのコメント:

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

他の種類の拡張メンバーが役立つ可能性があることをますます認識しているため、Orcasの後にこの問題に戻ります。ただし、保証はありません!

編集:

Madsが 同じ記事 でもっと書いていることに気付いた。

次のリリースではこれを行わないことを報告して申し訳ありません。私たちは計画の中で拡張メンバーを非常に真剣に考え、それらを正しくしようと多くの努力を費やしましたが、最終的には十分にスムーズにすることができず、他の興味深い機能に道を譲ることにしました。

これは、将来のリリースのためにまだ調査中です。役立つのは、適切な設計を促進するのに役立つかなりの説得力のあるシナリオを入手できればです。


この機能は現在、C#8.0の(潜在的に)テーブルにあります。 Madsが実装についてもう少し説明します here

147
Jacob Krall

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

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から 暗黙の変換演算子 を宣言し、は目的の+演算子を宣言します。この方法で、StringBuilderReceiveImportantMessageに渡すことができ、StringBuilderWrapperに暗黙的に変換され、+演算子を使用できます。

この事実を発信者にわかりやすくするために、ReceiveImportantMessageStringBuilderとして取ると宣言し、次のようなコードを使用できます。

  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());

post を作成して、IComparableをより理解しやすくするために、同様のアプローチを使用しました。

55
Jordão

現在、これは不可能だと思われます。MicrosoftConnectのこの機能を要求するオープンフィードバックの問題があります。

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

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

8
Dylan Beattie

ハァッ! 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);

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

1
david van brink

演算子を実行することはできませんが、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
Chuck Rostance

ラッパーと拡張機能でリギングすることは可能ですが、適切に行うことはできません。目的を完全に無効にするゴミで終わります。私はここでそれを行う投稿をどこかに持っていますが、それは価値がありません。

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

0
will motil