Toán tử quá tải với các phương thức mở rộng C #


174

Tôi đang cố gắng sử dụng các phương thức mở rộng để thêm quá tải toán tử vào lớp C # StringBuilder. Cụ thể, được đưa ra StringBuilder sb, tôi muốn sb += "text"trở nên tương đương với sb.Append("text").

Đây là cú pháp để tạo một phương thức mở rộng cho StringBuilder:

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

Nó thành công thêm blahphương thức mở rộng cho StringBuilder.

Thật không may, quá tải toán tử dường như không hoạt động:

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

Trong số các vấn đề khác, từ khóa thiskhông được phép trong bối cảnh này.

Có thể thêm quá tải toán tử thông qua các phương thức mở rộng có thể? Nếu vậy, cách thích hợp để đi về nó là gì?


4
Mặc dù điều này lúc đầu có vẻ như là một ý tưởng hay, hãy xem xét var otherSb = sb + "hi";
hatchet - được thực hiện với SOverflow

Câu trả lời:


150

Điều này hiện không thể thực hiện được, bởi vì các phương thức mở rộng phải nằm trong các lớp tĩnh và các lớp tĩnh không thể có quá tải toán tử.

Mads Torgersen, C # Language PM nói:

... Để phát hành Orcas, chúng tôi đã quyết định áp dụng cách tiếp cận thận trọng và chỉ thêm các phương thức mở rộng thông thường, trái với các thuộc tính mở rộng, sự kiện, toán tử, phương thức tĩnh, v.v. một thiết kế cú pháp tối thiểu mà không thể dễ dàng bắt chước đối với một số loại thành viên khác.

Chúng tôi ngày càng nhận thức được rằng các loại thành viên khuyến nông khác có thể hữu ích, và vì vậy chúng tôi sẽ trở lại vấn đề này sau Orcas. Không có đảm bảo, mặc dù!

Biên tập:

Tôi chỉ nhận thấy, Mads đã viết nhiều hơn trong cùng một bài viết :

Tôi rất tiếc phải báo cáo rằng chúng tôi sẽ không làm điều này trong phiên bản tiếp theo. Chúng tôi đã rất coi trọng các thành viên khuyến nông trong kế hoạch của mình và đã nỗ lực rất nhiều để cố gắng làm cho họ đúng, nhưng cuối cùng, chúng tôi không thể làm cho nó đủ suôn sẻ, và quyết định nhường chỗ cho các tính năng thú vị khác.

Điều này vẫn còn trên radar của chúng tôi cho các bản phát hành trong tương lai. Điều gì sẽ giúp là nếu chúng ta có được một số lượng lớn các kịch bản hấp dẫn có thể giúp thúc đẩy thiết kế phù hợp.


Tính năng này hiện đang ở trên bàn (có khả năng) cho C # 8.0. Mads nói thêm một chút về việc thực hiện nó ở đây .


Trang này đã được gỡ xuống; vấn đề này vẫn chưa được giải quyết.
Chris Moschini

17
Quá tệ. Tôi chỉ muốn thêm một toán tử để nhân TimeSpan với giá trị vô hướng ... :(
Filip Skakun

Tôi đã hy vọng thực hiện khái niệm tương tự này để Stringchuyển sang PowerShell ScriptBlock.
Trevor Sullivan

Điều đó có nghĩa là tôi không thể quá tải% để trở thành booleans xoren? :( chết lúc true.Xor(false)đó
SparK

3
@SparK ^là toán tử xor trong C #
Jacob Krall

57

Nếu bạn kiểm soát những nơi bạn muốn sử dụng "toán tử mở rộng" này (mà bạn thường làm với các phương thức tiện ích mở rộng), bạn có thể làm một cái gì đó như thế này:

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

} 

Các StringBuilderWrapperlớp tuyên bố một nhà điều hành chuyển đổi ngầm từ StringBuilder tuyên bố mong muốn +khai thác. Bằng cách này, a StringBuildercó thể được chuyển đến ReceiveImportantMessage, sẽ được chuyển đổi âm thầm thành a StringBuilderWrapper, nơi +toán tử có thể được sử dụng.

Để làm cho sự thật này minh bạch hơn với người gọi, bạn có thể tuyên bố ReceiveImportantMessagelà lấy StringBuildervà chỉ sử dụng mã như thế này:

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

Hoặc, để sử dụng nội tuyến nơi bạn đã sử dụng a StringBuilder, bạn chỉ cần thực hiện việc này:

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

Tôi đã tạo một bài viết về việc sử dụng một cách tiếp cận tương tự để dễ IComparablehiểu hơn.


2
@Leon: Tôi thực sự có ý định sáng tác nó, không kế thừa từ nó. Dù sao, tôi không thể thừa hưởng từ nó vì nó được niêm phong.
Jordão

5
@Leon: Đó là trung tâm của kỹ thuật này. Tôi có thể làm điều đó bởi vì có một toán tử chuyển đổi ngầm định được khai báo trong StringBuilderWrapperđó làm cho nó có thể.
Jordão

1
@pylover: Bạn nói đúng, điều này đòi hỏi phải tạo một loại mới, nó sẽ bao bọc StringBuilderloại đó và cung cấp một toán tử chuyển đổi ẩn từ nó. Sau đó, nó có thể được sử dụng với chuỗi ký tự, như được minh họa trong ví dụ:sb += "Hello World!";
Jordão

2
Tôi có thể đề nghị sau đó, để thêm một phương thức mở rộng vào String: PushIndent(" ".X(4))(cũng có thể được gọi Times). Hoặc có thể sử dụng hàm tạo này : PushIndent(new String(' ', 4)).
Jordão

1
@ Jordão: Câu trả lời tuyệt vời;)
Vinicius

8

Có vẻ như điều này hiện không thể thực hiện được - có một vấn đề phản hồi mở yêu cầu chính tính năng này trên Microsoft Connect:

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

cho thấy nó có thể xuất hiện trong phiên bản tương lai nhưng không được triển khai cho phiên bản hiện tại.


Chính xác thì ý bạn là gì, "hiện tại không thể?" Nó phải có thể có trong CLR vì F # hỗ trợ mọi thứ mở rộng.
Matthew Olenik

1
Tôi nghĩ rằng anh ta có nghĩa là không thể có trong C #, không phải CLR. Toàn bộ phương thức mở rộng là một thủ thuật biên dịch C #.
tofi9

1
Liên kết đã chết bây giờ.
CBHacking

1

Mặc dù các toán tử không thể thực hiện được, nhưng bạn luôn có thể tạo các phương thức Thêm (hoặc Ghép), Trừ và So sánh ....

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

Hừ! Tôi đã tìm kiếm "quá tải toán tử mở rộng" với cùng một mong muốn, cho sb + = (điều).

Sau khi đọc câu trả lời ở đây (và thấy rằng câu trả lời là "không"), đối với các nhu cầu cụ thể của tôi, tôi đã sử dụng một phương pháp mở rộng kết hợp sb.AppendLine và sb.AppendFormat và trông gọn gàng hơn.

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

}

Và vì thế,

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

Không phải là một câu trả lời chung chung, nhưng có thể được quan tâm cho những người tìm kiếm trong tương lai nhìn vào loại điều này.


4
Tôi nghĩ rằng phương pháp mở rộng của bạn sẽ đọc tốt hơn nếu bạn đặt tên AppendLinethay vì Line.
DavidRR

0

Có thể rigg nó với một trình bao bọc và phần mở rộng nhưng không thể làm điều đó đúng. Bạn kết thúc với rác mà hoàn toàn đánh bại mục đích. Tôi có một bài viết ở đâu đó trên đây mà làm điều đó, nhưng nó vô giá trị.

Btw Tất cả các chuyển đổi số tạo rác trong trình tạo chuỗi cần được sửa. Tôi đã phải viết một trình bao bọc cho cái nào hoạt động và tôi sử dụng nó. Đó là giá trị dễ hiểu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.