ฉันจะขยายคลาสด้วยวิธีการขยาย c # ได้อย่างไร


98

สามารถใช้วิธีการขยายกับคลาสได้หรือไม่?

ตัวอย่างเช่นขยาย DateTime เพื่อรวมเมธอด Tomorrow () ที่สามารถเรียกใช้เช่น:

DateTime.Tomorrow();

ฉันรู้ว่าฉันสามารถใช้ได้

static DateTime Tomorrow(this Datetime value) { //... }

หรือ

public static MyClass {
  public static Tomorrow() { //... }
}

สำหรับผลลัพธ์ที่คล้ายกัน แต่ฉันจะขยาย DateTime เพื่อให้ฉันสามารถเรียก DateTime.Tomorrow ได้อย่างไร

คำตอบ:


69

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

ไม่มีอะไรหยุดคุณจากการสร้างวิธีการช่วยเหลือแบบคงที่ของคุณเองเช่นนี้:

static class DateTimeHelper
{
    public static DateTime Tomorrow
    {
        get { return DateTime.Now.AddDays(1); }
    }
}

ซึ่งคุณจะใช้ดังนี้:

DateTime tomorrow = DateTimeHelper.Tomorrow;

7
ห๊ะ woot? เว้นแต่จะมีการใช้งานภายใน 6 เดือนหลังจากนี้และคำตอบของKumu ที่นั่นดูเหมือนว่าจะไม่สมบูรณ์จริงๆ
cregox

4
@Cawas สิ่งนี้ไม่สมบูรณ์แอนดรูกำลังแสดงวิธีการทำสิ่งนี้ด้วยตัวช่วยแบบคงที่ไม่ใช่ด้วยวิธีการขยาย (เนื่องจากไม่มีอินสแตนซ์)
Nick N.

1
คุณใช่นิค ฉันชอบวิธีการขยายมากกว่า! ;)
cregox

2
มีอะไรเกี่ยวกับextensionmethod.net/csharp/datetime ? IMHO ตัวอย่างที่ดีกว่าสำหรับการลดเส้นโค้งการเรียนรู้คือแอปพลิเคชันจริงที่มีซอร์สโค้ดเต็มรูปแบบและรูปแบบที่ดี
Kiquenet

3
ปัญหาเกี่ยวกับรหัสนี้คือใช้ได้เฉพาะกับ DateTime.Now ไม่ใช่วัตถุ DateTime ใด ๆ ในฐานะยูทิลิตี้หนึ่งอาจต้องการใช้เพื่อกำหนดวันถัดจากวันก่อนหน้า (หรือในอนาคต) ไม่ต้องพูดถึง DateTime ตอนนี้จะถูกกำหนดทุกครั้งที่คุณเรียกมันว่า ...
MintGrowth

182

ใช้วิธีขยาย

เช่น:

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
            return date.AddDays(1);
        }    
    }
}

การใช้งาน:

DateTime.Now.Tomorrow();

หรือ

AnyObjectOfTypeDateTime.Tomorrow();

2
คำตอบของShuggyยังทำลายความสว่างในการแก้ปัญหานี้
cregox

8
อย่าลืม 'ใช้ ExtensionMethods;' ที่ด้านบนของเอกสารของคุณสำหรับสิ่งนี้
Luke Alderton

ทำไมฉันถึงทำ DateTime.Tomorrow () ไม่ได้
lawphotog

สวัสดี lawphotog ส่วนขยายนี้ต้องการวัตถุที่นี่ DateTime เป็นโครงสร้างไม่ใช่วัตถุ
Kumu

4
ตามที่กล่าวไว้ในความคิดเห็นก่อนหน้านี้ (เห็นได้ชัดว่าไม่ชัดเจนพอสำหรับฉัน) คุณจะไม่สามารถใช้DateTime.Tomorrow()เป็นวิธีการขยายได้เฉพาะกับ INSTANCES ของคลาสและโครงสร้างคลาสเท่านั้น "ขยาย" วิธีการคงอยู่ในระดับ struc ทำตามคำตอบที่แอนดรูหรือคำตอบของ Shuggy
Alex

18

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

ด้วยเหตุนี้ผลประโยชน์ส่วนใหญ่จึงหายไปเมื่อคุณสร้าง 'วิธีการขยายแบบคงที่' เนื่องจากจะทำให้ผู้อ่านสับสนมากกว่าวิธีการขยาย (เนื่องจากดูเหมือนว่าจะมีคุณสมบัติครบถ้วน แต่ไม่ได้กำหนดไว้ในคลาสนั้นจริง) สำหรับการไม่ได้รับการสังเคราะห์ (ความสามารถในการเชื่อมต่อการโทรในรูปแบบที่คล่องแคล่วภายใน Linq เป็นต้น)

เนื่องจากคุณจะต้องนำส่วนขยายเข้าสู่ขอบเขตด้วยการใช้งานฉันขอยืนยันว่าการสร้างนั้นง่ายและปลอดภัยกว่า:

public static class DateTimeUtils
{
    public static DateTime Tomorrow { get { ... } }
}

จากนั้นใช้สิ่งนี้ในรหัสของคุณผ่าน:

WriteLine("{0}", DateTimeUtils.Tomorrow)

11

สิ่งที่ใกล้เคียงที่สุดที่ฉันจะได้รับคำตอบคือการเพิ่มวิธีการขยายลงในSystem.Typeวัตถุ ไม่สวย แต่ก็ยังน่าสนใจ

public static class Foo
{
    public static void Bar()
    {
        var now = DateTime.Now;
        var tomorrow = typeof(DateTime).Tomorrow();
    }

    public static DateTime Tomorrow(this System.Type type)
    {
        if (type == typeof(DateTime)) {
            return DateTime.Now.AddDays(1);
        } else {
            throw new InvalidOperationException();
        }
    }
}

มิฉะนั้น IMO Andrew และ ShuggyCoUk จะมีการใช้งานที่ดีกว่า


มีปัญหาเกี่ยวกับแนวทางนี้ การต้องพิมพ์ "typeof (... )" จึงไม่สะดวกและด้วย Intellisense คุณจะเห็นนามสกุลทุกประเภท ยังคงเป็นแนวทางที่น่าสนใจที่ฉันไม่เคยนึกถึง +1
Meta-Knight

@ Meta-Knight True นั่นเป็นเหตุผลว่าทำไมโดยส่วนตัวแล้วฉันชอบคำตอบของอีกฝ่ายมากกว่า คำตอบของฉันจะมีไวยากรณ์ที่ใกล้เคียงที่สุดกับคำถาม OP แต่ไม่ใช่วิธีที่ดีที่สุดในการแก้ปัญหานี้
Adrian Godong

Typeสามารถแทนที่ด้วยประเภทอื่น ๆ ที่จำเป็น ฉันใช้มันFromและทำงานได้อย่างสมบูรณ์ ดังนั้นฉันเดาว่าคำตอบนี้เป็นเรื่องทั่วไป แต่ถูกต้อง
Katia

3

ฉันจะทำเช่นเดียวกับ Kumu

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
           return date.AddDays(1);
        }    
    }
}

แต่เรียกแบบนี้ DateTime ใหม่ () พรุ่งนี้ ();

คิดว่ามันทำให้เห็นมากกว่า DateTime.Now.Tomorrow ();


1
และคุณพลาดโอกาสที่จะเขียนเป็นความคิดเห็นในคำตอบของ Kumu! : P
cregox

3

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

ตรวจสอบตัวอย่างเต็มได้ที่นี่ http://www.dotnetreaders.com/articles/Extension_methods_in_C-sharp.net,Methods_in_C_-sharp/201

ตัวอย่าง:

class Extension
    {
        static void Main(string[] args)
        {
            string s = "sudhakar";
            Console.WriteLine(s.GetWordCount());
            Console.ReadLine();
        }

    }
    public static class MyMathExtension
    {

        public static int GetWordCount(this System.String mystring)
        {
            return mystring.Length;
        }
    }

3

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

  1. คุณไม่สามารถมีสิ่งที่เป็นส่วนตัวหรือได้รับการปกป้อง - ช่องวิธีการ ฯลฯ

  2. ต้องเป็นคลาสคงที่เช่นเดียวกับในpublic static class....

  3. เมธอดเท่านั้นที่สามารถอยู่ในคลาสได้และต้องเป็นแบบคงที่สาธารณะ

  4. คุณไม่สามารถมีวิธีการคงที่แบบเดิมได้ - วิธีที่ไม่มีอาร์กิวเมนต์นี้ไม่ได้รับอนุญาต

  5. ต้องเริ่มต้นวิธีการทั้งหมด:

    วิธีการ ReturnType แบบคงที่สาธารณะ (ชื่อคลาสนี้ _this, ... )

ดังนั้นอาร์กิวเมนต์แรกจึงเป็นข้อมูลอ้างอิงนี้เสมอ

มีปัญหาโดยปริยายที่สิ่งนี้สร้างขึ้น - หากคุณเพิ่มวิธีการที่ต้องใช้การล็อกประเภทใด ๆ คุณจะไม่สามารถระบุได้ในระดับชั้นเรียน โดยปกติแล้วคุณจะมีการล็อกระดับอินสแตนซ์ส่วนตัว แต่ไม่สามารถเพิ่มฟิลด์ส่วนตัวใด ๆ ได้ทำให้คุณมีตัวเลือกที่น่าอึดอัดใจมากเช่นการจัดให้เป็นแบบคงที่สาธารณะในชั้นเรียนภายนอกบางประเภทเป็นต้น ป้ายC # ภาษามีชนิดของการเปิดที่ไม่ดีในการออกแบบเหล่านี้

วิธีแก้ปัญหาคือการใช้วิธีขยายชั้นเรียนของคุณเป็นเพียงซุ้มในระดับปกติและทุกวิธีการแบบคงที่ในระดับขยายของคุณเพียงแค่โทรหาชั้นที่แท้จริงอาจจะใช้ซิงเกิล


2

น่าเสียดายที่คุณไม่สามารถทำเช่นนั้นได้ ฉันเชื่อว่ามันจะมีประโยชน์ เป็นธรรมชาติมากกว่าที่จะพิมพ์:

DateTime.Tomorrow

กว่า:

DateTimeUtil.Tomorrow

ด้วยคลาส Util คุณต้องตรวจสอบการมีอยู่ของวิธีการคงที่ในสองคลาสที่แตกต่างกันแทนที่จะเป็นหนึ่งคลาส


2

เราได้ปรับปรุงคำตอบของเราพร้อมคำอธิบายโดยละเอียดตอนนี้เข้าใจง่ายขึ้นเกี่ยวกับวิธีการขยาย

วิธีการขยาย : เป็นกลไกที่เราสามารถขยายพฤติกรรมของคลาสที่มีอยู่ได้โดยไม่ต้องใช้คลาสย่อยหรือแก้ไขหรือคอมไพล์คลาสหรือโครงสร้างเดิม

เราสามารถขยายคลาสแบบกำหนดเองคลาส. net framework เป็นต้น

วิธีการขยายเป็นวิธีพิเศษแบบคงที่ซึ่งกำหนดไว้ในคลาสคงที่

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

ด้านล่างนี้คือตัวอย่าง

// นี่คือคลาสเครื่องคิดเลขที่มีอยู่ซึ่งมีเพียงวิธีเดียวเท่านั้น (เพิ่ม)

public class Calculator 
{
    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }

}

// Below is the extension class which have one extension method.  
public static class Extension
{
    // It is extension method and it's first parameter is a calculator class.It's behavior is going to extend. 
    public static double Division(this Calculator cal, double num1,double num2){
       return num1 / num2;
    }   
}

// We have tested the extension method below.        
class Program
{
    static void Main(string[] args)
    {
        Calculator cal = new Calculator();
        double add=cal.Add(10, 10);
        // It is a extension method in Calculator class.
        double add=cal.Division(100, 10)

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