ผู้ประกอบการมากไปด้วยวิธีการขยาย C #


174

ฉันกำลังพยายามใช้วิธีการขยายเพื่อเพิ่มตัวดำเนินการเกินพิกัดให้กับStringBuilderคลาสC # โดยเฉพาะได้รับStringBuilder sbฉันต้องการที่จะกลายเป็นเทียบเท่ากับsb += "text"sb.Append("text")

นี่คือไวยากรณ์สำหรับการสร้างวิธีการขยายสำหรับStringBuilder:

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

มันประสบความสำเร็จเพิ่มวิธีขยายไปยังblahStringBuilder

น่าเสียดายที่การใช้งานมากเกินไปดูเหมือนจะไม่ทำงาน:

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

ในประเด็นอื่น ๆ คำหลักthisไม่ได้รับอนุญาตในบริบทนี้

มีการเพิ่มโอเปอเรเตอร์เกินพิกัดผ่านวิธีการขยายหรือไม่ ถ้าอย่างนั้นเป็นวิธีที่เหมาะสมที่จะไปเกี่ยวกับมันคืออะไร?


4
แม้ว่าสิ่งนี้ในตอนแรกดูเหมือนจะเป็นความคิดที่ดี แต่โปรดพิจารณา var otherSb = sb + "hi";
ขวาน - จบด้วย SOverflow

คำตอบ:


150

ไม่สามารถทำได้ในขณะนี้เนื่องจากวิธีการขยายต้องอยู่ในคลาสสแตติกและคลาสสแตติกไม่สามารถมีโอเปอเรเตอร์โอเวอร์โหลดได้

Mads Torgersen, C # Language PM พูดว่า:

... สำหรับการปล่อย Orcas เราตัดสินใจที่จะใช้วิธีการระมัดระวังและเพิ่มวิธีการขยายปกติเท่านั้นซึ่งตรงข้ามกับคุณสมบัติส่วนขยายเหตุการณ์ผู้ประกอบการวิธีการคงที่ ฯลฯ ฯลฯ วิธีการขยายปกติเป็นสิ่งที่เราต้องการสำหรับ LINQ และพวกเขามี การออกแบบขั้นต่ำที่ไม่สามารถเลียนแบบได้ง่ายสำหรับสมาชิกประเภทอื่น ๆ

เราเริ่มรับรู้มากขึ้นว่าสมาชิกส่วนขยายประเภทอื่นอาจเป็นประโยชน์และเราจะกลับไปที่ปัญหานี้หลังจาก Orcas ไม่มีการรับประกันแม้ว่า!

แก้ไข:

ฉันเพิ่งสังเกตเห็น Mads เขียนเพิ่มเติมในบทความเดียวกัน :

ฉันขอโทษที่ต้องรายงานว่าเราจะไม่ทำเช่นนี้ในรุ่นถัดไป เราให้ความสำคัญกับสมาชิกส่วนขยายอย่างจริงจังในแผนของเราและใช้ความพยายามอย่างมากในการทำให้พวกเขาถูกต้อง แต่ในที่สุดเราก็ไม่สามารถทำให้มันราบรื่นพอและตัดสินใจที่จะหลีกเลี่ยงคุณสมบัติที่น่าสนใจอื่น ๆ

นี่ยังคงเป็นเรดาห์ของเราสำหรับการเปิดตัวในอนาคต สิ่งที่จะช่วยได้คือถ้าเราได้รับสถานการณ์ที่น่าสนใจซึ่งสามารถช่วยขับเคลื่อนการออกแบบที่เหมาะสม


ปัจจุบันคุณลักษณะนี้อยู่ในตาราง (อาจ) สำหรับ C # 8.0 Mads พูดอีกเล็กน้อยเกี่ยวกับการดำเนินการนั้นที่นี่


หน้านี้ได้ถูกลบออกไปแล้ว; ปัญหานี้ยังไม่สามารถแก้ไขได้
Chris Moschini

17
เลวร้ายเกินไป. ฉันแค่ต้องการเพิ่มโอเปอเรเตอร์เพื่อคูณ TimeSpan ด้วยค่าสเกลาร์ ... :(
Filip Skakun

ผมก็หวังว่าจะใช้แนวคิดเดียวกันนี้จะโยนStringไป ScriptBlockPowerShell
Trevor Sullivan

นั่นหมายความว่าฉันไม่สามารถเกิน%% ที่จะเป็น xor betwen booleans? :( ตายตอนtrue.Xor(false)นั้น
SparK

3
@SparK ^เป็นผู้ดำเนินการ xor ใน C #
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 และประกาศที่ต้องการ+ประกอบการ วิธีนี้StringBuilderสามารถส่งผ่านไปยังReceiveImportantMessageซึ่งจะถูกแปลงเป็น a 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
@Leon: ฉันตั้งใจจะแต่งมันจริงๆไม่ใช่สืบทอดมาจากมัน อย่างไรก็ตามฉันไม่สามารถสืบทอดจากมันตั้งแต่มันถูกปิดผนึก
Jordão

5
@Leon: นั่นคือหัวใจของเทคนิคนี้ ฉันสามารถทำได้เพราะมีตัวดำเนินการแปลงโดยนัยซึ่งประกาศStringBuilderWrapperว่าเป็นไปได้
Jordão

1
@pylover: คุณพูดถูกต้องต้องมีการสร้างประเภทใหม่ที่จะทำการตัดคำStringBuilderและให้โอเปอเรเตอร์การแปลงโดยนัยจากมัน หลังจากนั้นสามารถใช้กับตัวอักษรสตริงได้ดังที่แสดงในตัวอย่าง:sb += "Hello World!";
Jordão

2
ฉันขอแนะนำให้เพิ่มวิธีการขยายไปยังString: PushIndent(" ".X(4))(อาจเรียกได้อีกด้วยTimes) PushIndent(new String(' ', 4))หรืออาจจะใช้คอนสตรัคนี้:
Jordão

1
@ Jordão: คำตอบที่ดี;)
Vinicius

8

ดูเหมือนว่าสิ่งนี้จะไม่สามารถทำได้ในขณะนี้ - มีปัญหาข้อเสนอแนะแบบเปิดที่ขอคุณลักษณะนี้มากใน Microsoft Connect

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

การแนะนำว่าอาจปรากฏในรุ่นอนาคต แต่ไม่ได้ใช้กับเวอร์ชันปัจจุบัน


คุณหมายถึงอะไรกันแน่ "ไม่สามารถทำได้ในขณะนี้" จะต้องเป็นไปได้ใน CLR เนื่องจาก F # รองรับทุกอย่างของส่วนขยาย
Matthew Olenik

1
ฉันคิดว่าเขาหมายความว่าเป็นไปไม่ได้ใน C # ไม่ใช่ CLR สิ่งที่วิธีการขยายทั้งหมดเป็นเคล็ดลับคอมไพเลอร์ C # ต่อไป
tofi9

1
ลิงค์เสียชีวิตแล้ว
CBHacking

1

แม้ว่ามันจะเป็นไปไม่ได้ที่จะใช้ตัวดำเนินการ แต่คุณสามารถสร้างวิธีเพิ่ม (หรือ Concat), ลบและเปรียบเทียบได้เสมอ ...

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 + = (สิ่ง)

หลังจากอ่านคำตอบที่นี่ (และเมื่อเห็นว่าคำตอบคือ "ไม่") สำหรับความต้องการเฉพาะของฉันฉันไปด้วยวิธีการต่อซึ่งรวม 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

0

เป็นไปได้ที่จะ rigg มันด้วยเสื้อคลุมและส่วนขยาย แต่มันเป็นไปไม่ได้ที่จะทำอย่างถูกต้อง คุณจบลงด้วยขยะที่ทำลายวัตถุประสงค์ทั้งหมด ฉันมีโพสต์บางแห่งที่นี่ที่มัน แต่มันไร้ค่า

Btw การแปลงตัวเลขทั้งหมดสร้างขยะในตัวสร้างสตริงที่ต้องได้รับการแก้ไข ฉันต้องเขียนเสื้อคลุมสำหรับสิ่งที่ใช้งานได้และฉันใช้มัน นั่นคือมูลค่าการอ่าน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.