มีวิธีใดบ้างใน C # ที่จะแทนที่เมธอดคลาสด้วยเมธอดส่วนขยาย?


100

มีบางครั้งที่ฉันต้องการแทนที่เมธอดในคลาสด้วยเมธอดส่วนขยาย มีวิธีใดบ้างที่จะทำใน C #?

ตัวอย่างเช่น:

public static class StringExtension
{
    public static int GetHashCode(this string inStr)
    {
        return MyHash(inStr);
    }
}

กรณีที่ฉันต้องการทำเช่นนี้คือสามารถจัดเก็บแฮชของสตริงลงในฐานข้อมูลและมีการใช้ค่าเดียวกันกับคลาสทั้งหมดที่ใช้แฮชของคลาสสตริง (เช่นพจนานุกรมเป็นต้น) ตั้งแต่ ไม่รับประกันว่าอัลกอริทึมการแฮช. Net ในตัวจะเข้ากันได้จาก Framework รุ่นหนึ่งไปอีกเวอร์ชันหนึ่งฉันต้องการแทนที่ด้วยของฉันเอง

มีอีกกรณีหนึ่งที่ฉันพบว่าฉันต้องการแทนที่เมธอดคลาสด้วยเมธอดส่วนขยายเช่นกันดังนั้นจึงไม่เฉพาะเจาะจงกับคลาสสตริงหรือเมธอด GetHashCode

ฉันรู้ว่าฉันสามารถทำสิ่งนี้ได้ด้วยการแบ่งคลาสย่อยออกจากคลาสที่มีอยู่ แต่มันจะสะดวกมากที่จะสามารถทำกับส่วนขยายได้ในหลาย ๆ กรณี


เนื่องจากพจนานุกรมเป็นโครงสร้างข้อมูลในหน่วยความจำความแตกต่างอะไรที่ทำให้อัลกอริทึมการแฮชเปลี่ยนจากเวอร์ชันของเฟรมเวิร์กไปเป็นเวอร์ชันถัดไป หากเวอร์ชันของเฟรมเวิร์กมีการเปลี่ยนแปลงแสดงว่าแอปพลิเคชันถูกรีสตาร์ทและพจนานุกรมได้รับการสร้างขึ้นใหม่อย่างชัดเจน
David Nelson

คำตอบ:


94

ไม่; วิธีการขยายไม่เคยให้ความสำคัญกับวิธีการอินสแตนซ์ที่มีลายเซ็นที่เหมาะสมและไม่เคยมีส่วนร่วมในความหลากหลาย ( GetHashCodeเป็นvirtualวิธีการ)


"... ไม่เคยมีส่วนร่วมในความหลากหลาย (GetHashCode เป็นวิธีการเสมือนจริง)" คุณช่วยอธิบายอีกทางหนึ่งได้ไหมว่าทำไมวิธีการขยายจึงไม่เข้าร่วมในความหลากหลายเนื่องจากเป็นเสมือนจริง ฉันมีปัญหาในการดูความเชื่อมโยงกับข้อความทั้งสองนี้
Alex

3
@ อเล็กซ์โดยกล่าวถึงเสมือนฉันเพียงแค่ชี้แจงว่ามันหมายถึงอะไร ในการใช้ GetHashCode แทบทั้งหมดไม่ทราบชนิดของคอนกรีตดังนั้นความหลากหลายจึงอยู่ในการเล่น ดังนั้นวิธีการขยายจะไม่ช่วยแม้ว่าจะให้ความสำคัญในคอมไพเลอร์ทั่วไปก็ตาม สิ่งที่ OP ต้องการจริงๆคือการปะลิง c # และ. net ใดไม่อำนวยความสะดวก
Marc Gravell

4
นั่นเป็นเรื่องน่าเศร้าใน C # มีวิธีการที่ไม่เข้าใจผิดมากมายเช่น.ToString()ในอาร์เรย์ เป็นคุณสมบัติที่จำเป็นอย่างแน่นอน
Hi-Angel

ทำไมคุณไม่ได้รับข้อผิดพลาดของคอมไพเลอร์ เช่นฉันพิมพ์ namespace System { public static class guilty { public static string ToLower(this string s) { return s.ToUpper() + " I'm Malicious"; } } }
LowLevel

@LowLevel คุณจะทำไม?
Marc Gravell

7

หากวิธีนี้มีลายเซ็นที่แตกต่างกันก็สามารถทำได้ - ในกรณีของคุณ: ไม่

แต่อย่างอื่นคุณต้องใช้มรดกเพื่อทำสิ่งที่คุณกำลังมองหา


2

เท่าที่ฉันรู้คำตอบคือไม่เพราะวิธีการขยายไม่ใช่อินสแตนซ์มันเหมือนกับสิ่งอำนวยความสะดวก Intellisense สำหรับฉันที่ให้คุณเรียกวิธีการคงที่โดยใช้อินสแตนซ์ของคลาส ฉันคิดว่าวิธีแก้ปัญหาของคุณอาจเป็นตัวสกัดกั้นที่สกัดกั้นการดำเนินการของวิธีการเฉพาะ (เช่น GetHashCode ()) และทำอย่างอื่นในการใช้ตัวสกัดกั้นดังกล่าว (เช่นเดียวกับที่ Castle Project มีให้) วัตถุทั้งหมดควรถูกสร้างขึ้นโดยใช้ โรงงานอ็อบเจ็กต์ (หรือคอนเทนเนอร์ IoC ใน Castle) เพื่อให้อินเตอร์เฟสของพวกเขาสามารถถูกดักจับผ่านพร็อกซีแบบไดนามิกที่สร้างขึ้นในรันไทม์ (Caslte ยังให้คุณสกัดกั้นสมาชิกเสมือนของคลาส)


0

ฉันพบวิธีเรียกใช้เมธอดส่วนขยายที่มีลายเซ็นเดียวกันกับเมธอดคลาส แต่ดูเหมือนจะไม่สวยหรูมากนัก เมื่อเล่นกับวิธีการขยายฉันสังเกตเห็นพฤติกรรมบางอย่างที่ไม่มีเอกสาร โค้ดตัวอย่าง:

public static class TestableExtensions
{
    public static string GetDesc(this ITestable ele)
    {
        return "Extension GetDesc";
    }

    public static void ValDesc(this ITestable ele, string choice)
    {
        if (choice == "ext def")
        {
            Console.WriteLine($"Base.Ext.Ext.GetDesc: {ele.GetDesc()}");
        }
        else if (choice == "ext base" && ele is BaseTest b)
        {
            Console.WriteLine($"Base.Ext.Base.GetDesc: {b.BaseFunc()}");
        }
    }

    public static string ExtFunc(this ITestable ele)
    {
        return ele.GetDesc();
    }

    public static void ExtAction(this ITestable ele, string choice)
    {
        ele.ValDesc(choice);
    }
}

public interface ITestable
{

}

public class BaseTest : ITestable
{
    public string GetDesc()
    {
        return "Base GetDesc";
    }

    public void ValDesc(string choice)
    {
        if (choice == "")
        {
            Console.WriteLine($"Base.GetDesc: {GetDesc()}");
        }
        else if (choice == "ext")
        {
            Console.WriteLine($"Base.Ext.GetDesc: {this.ExtFunc()}");
        }
        else
        {
            this.ExtAction(choice);
        }
    }

    public string BaseFunc()
    {
        return GetDesc();
    }
}

สิ่งที่ฉันสังเกตเห็นคือถ้าฉันเรียกวิธีที่สองจากภายในวิธีการขยายมันจะเรียกวิธีการขยายที่ตรงกับลายเซ็นแม้ว่าจะมีเมธอดคลาสที่ตรงกับลายเซ็นก็ตาม ตัวอย่างเช่นในโค้ดด้านบนเมื่อฉันเรียก ExtFunc () ซึ่งจะเรียก ele.GetDesc () ฉันจะได้รับสตริงส่งคืน "Extension GetDesc" แทนสตริง "Base GetDesc" ที่เราคาดหวัง

การทดสอบรหัส:

var bt = new BaseTest();
bt.ValDesc("");
//Output is Base.GetDesc: Base GetDesc
bt.ValDesc("ext");
//Output is Base.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext def");
//Output is Base.Ext.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext base");
//Output is Base.Ext.Base.GetDesc: Base GetDesc

วิธีนี้ช่วยให้คุณสามารถย้อนกลับไปมาระหว่างเมธอดคลาสและเมธอดส่วนขยายได้ตามต้องการ แต่ต้องการการเพิ่มเมธอด "pass-through" ที่ซ้ำกันเพื่อให้คุณเข้าสู่ "ขอบเขต" ที่คุณต้องการ ฉันเรียกมันว่าขอบเขตที่นี่เพราะขาดคำที่ดีกว่า หวังว่าจะมีคนแจ้งให้เราทราบว่าจริงๆแล้วมันเรียกว่าอะไร

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

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