C # Genişletme Yöntemleriyle Operatör Aşırı Yüklemesi


174

Ben C # StringBuildersınıfına bir operatör aşırı yük eklemek için uzantı yöntemleri kullanmaya çalışıyorum . Özellikle, verilen StringBuilder sb, sb += "text"eşdeğer olmak istiyorum sb.Append("text").

Aşağıdakiler için bir uzantı yöntemi oluşturmak için sözdizimi şöyledir StringBuilder:

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

blahUzantı yöntemini başarıyla ekler StringBuilder.

Ne yazık ki, operatör aşırı yüklemesi işe yaramıyor:

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

Diğer sorunların yanı sıra, thisbu bağlamda anahtar kelimeye izin verilmez.

Genişletme yöntemleri ile operatör aşırı yüklemeleri eklemek mümkün müdür? Eğer öyleyse, bunun için uygun yol nedir?


4
İlk başta bu harika bir fikir gibi görünse de, var otherSb = sb + "hi" yi düşünün;
balta - SOverflow ile haziran

Yanıtlar:


150

Uzantı yöntemleri statik sınıflarda olması gerektiğinden ve statik sınıflarda işleç aşırı yüklenmeleri bulunmadığından bu şu anda mümkün değildir.

Mads Torgersen, C # Dili Başbakanı diyor ki:

... Orcas sürümü için temkinli yaklaşımı kullanmaya ve uzatma özelliklerinin, olayların, operatörlerin, statik yöntemlerin vb. aksine düzenli uzatma yöntemlerini eklemeye karar verdik. diğer üye türlerin bazıları için kolayca taklit edilemeyen sözdizimsel olarak minimal bir tasarım.

Diğer tür uzatma üyelerinin yararlı olabileceğinin giderek daha fazla farkında oluyoruz ve bu nedenle Orcas'tan sonra bu konuya döneceğiz. Yine de garanti yok!

Düzenle:

Sadece fark ettim, Mads aynı makalede daha fazlasını yazdı :

Bir sonraki sürümde bunu yapmayacağımızı bildirdiğim için üzgünüm. Uzatma üyelerini planlarımızda çok ciddiye aldık ve onları düzeltmek için çok çaba harcadık, ancak sonunda yeterince pürüzsüz hale getiremedik ve diğer ilginç özelliklere yol vermeye karar verdik.

Bu, gelecekteki sürümler için hala radarımızda. Yardımcı olacak şey, doğru tasarımı yönlendirmeye yardımcı olabilecek iyi miktarda zorlayıcı senaryolar elde etmemizdir.


Bu özellik şu anda C # 8.0 için tabloda (potansiyel olarak) bulunmaktadır. Mads bunu burada uygulamaktan biraz daha bahsediyor .


Bu sayfa o zamandan beri kaldırılmıştır; bu sorun hala çözülmedi.
Chris Moschini

17
Çok kötü. Sadece bir TimeSpan'ı skaler bir değerle çarpmak için bir operatör eklemek istedim ... :(
Filip Skakun

Aynı konsepti Stringbir PowerShell'e uygulamak için uygulamayı umuyordum ScriptBlock.
Trevor Sullivan

Bu booleans arasında xor% olmak aşırı yük olamaz demek? :( true.Xor(false)o zaman öldü
SparK

3
@SparK ^C # xor operatörü
Jacob Krall

57

Bu "uzantı operatörünü" kullanmak istediğiniz yerleri (normalde uzantı yöntemleriyle zaten yaparsanız) kontrol ederseniz, şöyle bir şey yapabilirsiniz:

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

} 

StringBuilderWrapperSınıf bir beyan kapalı dönüştürme işleci bir mesafede StringBuilder ve istenen beyan +operatör. Bu şekilde, bir StringBuildergeçirilebilir ReceiveImportantMessagesessizce bir dönüştürülecektir olan StringBuilderWrapperyerlerde, +operatör kullanılabilir.

Bu gerçeği arayanlar için daha şeffaf hale getirmek için, aşağıdaki ReceiveImportantMessagegibi bir StringBuilderkod kullandığını belirtebilir ve kullanabilirsiniz:

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

Veya, a'yı zaten kullandığınız satır içi olarak kullanmak için StringBuilderşunları yapabilirsiniz:

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

Oluşturduğum bir yazı yapmak için benzer bir yaklaşım kullanarak ilgiliIComparableDaha anlaşılır .


2
@Leon: Gerçekten bestelemek istedim, miras almak değil. Her neyse, mühürlü olduğu için miras alamıyordum.
Jordão

5
@Leon: Bu tekniğin kalbi bu. Orada çünkü bir bunu yapabilir örtük dönüştürme operatör bildirilen StringBuilderWrapperbu mümkün kılar.
Jordão

1
@pylover: Haklısınız, bunun için türü saran StringBuilderve örtük bir dönüşüm operatörü sağlayan yeni bir tür oluşturulması gerekir. Bundan sonra, yapabilirsiniz : örnekte gösterildiği gibi, dize hazır kullanılmalıdırsb += "Hello World!";
Jordão

2
O zaman, bir uzantısı yöntemi eklemek için önerebilir miyim String: PushIndent(" ".X(4))(ayrıca çağrılabilir Times). Ya da belki bu kurucuyu kullanarak: PushIndent(new String(' ', 4)).
Jordão

1
@ Jordão: Harika cevap;)
Vinicius

8

Görünüşe göre bu şu anda mümkün değil - Microsoft Connect'te bu özelliği talep eden açık bir geri bildirim sorunu var:

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

gelecekteki bir sürümde görünebileceğini, ancak mevcut sürüm için uygulanmadığını gösterir.


Tam olarak ne demek istiyorsun, "şu anda mümkün değil mi?" CLR'de mümkün olmalıdır, çünkü F # her şeyi genişletmeyi destekler.
Matthew Olenik

1
Bence CLR'de değil C # 'da mümkün değil. Tüm uzantı yöntemleri şey zaten bir C # derleyici hile.
tofi9

1
Bağlantı şimdi öldü.
CBHacking

1

Operatörleri yapmak mümkün olmasa da, her zaman Ekle (veya Concat), Çıkarma ve Karşılaştırma yöntemleri oluşturabilirsiniz.

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

Hah! Ben aynı arzu ile "uzatma operatörü aşırı yükleme", sb + = (şey) için bakıyordu.

Buradaki yanıtları okuduktan (ve cevabın "hayır" olduğunu gördükten sonra, özel ihtiyaçlarım için, sb.AppendLine ve sb.AppendFormat'ı birleştiren ve her ikisinden de daha düzenli görünen bir uzantı yöntemiyle gittim.

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

}

Ve bu yüzden,

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

Genel bir cevap değil, ancak bu tür şeylere bakan gelecekteki arayanlar için ilgi çekici olabilir.


4
Bunun AppendLineyerine adlandırmanız durumunda uzantı yönteminizin daha iyi olacağını düşünüyorum Line.
DavidRR

0

Bir sargı ve uzantılarla donatmak mümkündür, ancak düzgün bir şekilde yapmak imkansızdır. Amacı tamamen yenen çöplerle bitiyorsun. Burada bir yere kadar bir yazı var, ama onun değersiz.

Btw Tüm sayısal dönüşümler, dize oluşturucuda düzeltilmesi gereken çöp oluşturur. İşe yarayan için bir sarıcı yazmak zorunda kaldım ve kullanıyorum. Bu göz atmaya değer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.