Overloading Operator dengan Metode Ekstensi C #

174

Saya mencoba menggunakan metode ekstensi untuk menambahkan kelebihan operater ke kelas C # StringBuilder. Khususnya, mengingat StringBuilder sb, saya ingin sb += "text"menjadi setara sb.Append("text").

Berikut sintaks untuk membuat metode ekstensi untuk StringBuilder:

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

Berhasil menambahkan blahmetode ekstensi ke StringBuilder.

Sayangnya, overloading operator tampaknya tidak berfungsi:

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

Di antara masalah lain, kata kunci thistidak diperbolehkan dalam konteks ini.

Apakah menambah kelebihan operator melalui metode ekstensi dimungkinkan? Jika demikian, apa cara yang tepat untuk melakukannya?

Jude Allred
sumber
4
Meskipun ini awalnya tampak seperti ide yang keren, pertimbangkan var otherSb = sb + "hai";
kapak - dilakukan dengan SOverflow

Jawaban:

150

Saat ini tidak memungkinkan, karena metode ekstensi harus dalam kelas statis, dan kelas statis tidak dapat memiliki kelebihan operator.

Mads Torgersen, C # Language PM mengatakan:

... untuk rilis Orcas, kami memutuskan untuk mengambil pendekatan yang hati-hati dan hanya menambahkan metode ekstensi reguler, sebagai lawan dari sifat ekstensi, peristiwa, operator, metode statis, dll. Metode ekstensi reguler adalah apa yang kami butuhkan untuk LINQ, dan mereka memiliki desain minimal secara sintaksis yang tidak dapat dengan mudah ditiru untuk beberapa jenis anggota lainnya.

Kami menjadi semakin sadar bahwa jenis penyuluh lainnya dapat berguna, dan karenanya kami akan kembali ke masalah ini setelah Orcas. Tidak ada jaminan!

Edit:

Saya baru saja memperhatikan, Mads menulis lebih banyak di artikel yang sama :

Saya menyesal melaporkan bahwa kami tidak akan melakukan ini di rilis berikutnya. Kami benar-benar menganggap serius anggota penyuluh dalam rencana kami, dan menghabiskan banyak upaya untuk memperbaikinya, tetapi pada akhirnya kami tidak dapat melakukannya dengan cukup lancar, dan memutuskan untuk memberi jalan pada fitur menarik lainnya.

Ini masih dalam radar kami untuk rilis mendatang. Apa yang akan membantu adalah jika kita mendapatkan sejumlah skenario menarik yang dapat membantu mendorong desain yang tepat.


Fitur ini saat ini di atas meja (berpotensi) untuk C # 8.0. Mads berbicara lebih banyak tentang penerapannya di sini .

Jacob Krall
sumber
Halaman ini sejak itu diturunkan; masalah ini masih belum terselesaikan.
Chris Moschini
17
Sangat buruk. Saya hanya ingin menambahkan operator untuk menggandakan TimeSpan dengan nilai skalar ... :(
Filip Skakun
Saya berharap untuk menerapkan konsep yang sama ini untuk melemparkan Stringke PowerShell ScriptBlock.
Trevor Sullivan
Itu berarti saya tidak bisa kelebihan% menjadi boolean xor antara? :( mati saat true.Xor(false)itu
SparK
3
@SparK ^adalah operator utama untuk C #
Jacob Krall
57

Jika Anda mengontrol tempat-tempat di mana Anda ingin menggunakan "operator ekstensi" ini (yang biasanya Anda lakukan dengan metode ekstensi), Anda dapat melakukan sesuatu seperti ini:

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;
  }

} 

The StringBuilderWrapperkelas menyatakan sebuah operator konversi implisit dari StringBuilder dan menyatakan yang diinginkan +operator. Dengan cara ini, a StringBuilderdapat diteruskan ke ReceiveImportantMessage, yang akan secara diam-diam dikonversi menjadi a StringBuilderWrapper, di mana +operator dapat digunakan.

Untuk membuat fakta ini lebih transparan bagi penelepon, Anda dapat mendeklarasikan ReceiveImportantMessagesebagai mengambil StringBuilderdan hanya menggunakan kode seperti ini:

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

Atau, untuk menggunakannya sebaris di mana Anda sudah menggunakan StringBuilder, Anda bisa melakukan ini:

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

Saya membuat posting tentang menggunakan pendekatan yang sama untuk membuat IComparablelebih mudah dimengerti.

Jordão
sumber
2
@Leon: Saya benar-benar bermaksud mengarangnya, bukan mewarisinya. Lagi pula, saya tidak bisa mewarisinya karena sudah disegel.
Jordão
5
@Leon: Itulah inti dari teknik ini. Saya bisa melakukan itu karena ada operator konversi implisit yang dinyatakan dalam StringBuilderWrapperyang memungkinkan.
Jordão
1
@pylover: Anda benar, ini membutuhkan pembuatan tipe baru, yang akan membungkus StringBuildertipe itu dan menyediakan operator konversi implisit darinya. Setelah itu, dapat digunakan dengan string literal, seperti yang ditunjukkan dalam contoh:sb += "Hello World!";
Jordão
2
Bolehkah saya menyarankan, untuk menambahkan metode ekstensi ke String: PushIndent(" ".X(4))(bisa juga dipanggil Times). Atau mungkin menggunakan konstruktor ini: PushIndent(new String(' ', 4)).
Jordão
1
@ Jordão: Jawaban luar biasa;)
Vinicius
8

Tampaknya saat ini tidak memungkinkan - ada masalah umpan balik terbuka yang meminta fitur ini di Microsoft Connect:

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

menyarankan itu mungkin muncul dalam rilis mendatang tetapi tidak diimplementasikan untuk versi saat ini.

Dylan Beattie
sumber
Apa sebenarnya yang Anda maksudkan, "saat ini tidak mungkin?" Itu harus dimungkinkan dalam CLR karena F # mendukung ekstensi segalanya.
Matthew Olenik
1
Saya pikir maksudnya adalah tidak mungkin dalam C #, bukan CLR. Keseluruhan metode ekstensi adalah trik kompiler C #.
tofi9
1
Tautan sudah mati sekarang.
CBHacking
1

Meskipun tidak mungkin melakukan operator, Anda selalu bisa membuat metode Tambah (atau Peduli), Kurangi, dan Bandingkan ....

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;    
        }
    }


}
Chuck Rostance
sumber
1

Hah! Saya mencari "extension operator overloading" dengan keinginan yang persis sama, untuk sb + = (hal).

Setelah membaca jawaban di sini (dan melihat bahwa jawabannya adalah "tidak"), untuk kebutuhan khusus saya, saya pergi dengan metode ekstensi yang menggabungkan sb.AppendLine dan sb.AppendFormat, dan terlihat lebih rapi daripada keduanya.

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

}

Sehingga,

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

Bukan jawaban umum, tetapi mungkin menarik bagi para pencari masa depan melihat hal semacam ini.

david van brink
sumber
4
Saya pikir metode ekstensi Anda akan membaca lebih baik jika Anda menamakannya AppendLinebukan Line.
DavidRR
0

Mungkin untuk rigg dengan bungkus dan ekstensi tetapi tidak mungkin melakukannya dengan benar. Anda berakhir dengan sampah yang benar-benar mengalahkan tujuannya. Saya punya posting di suatu tempat di sini yang melakukannya, tetapi itu tidak berharga.

Btw Semua konversi numerik membuat sampah di pembuat string yang perlu diperbaiki. Saya harus menulis pembungkus untuk apa yang berfungsi dan saya menggunakannya. Itu layak dibaca.

akan motil
sumber