คุณจะใช้ผู้รับมอบสิทธิ์ใน C # เมื่อใด [ปิด]


101

คุณใช้ตัวแทนใน C # อะไรบ้าง?


2
คุณหมายถึงผู้รับมอบสิทธิ์ในระบบประเภท. NET หรือไวยากรณ์ C # delegate? คุณหมายถึง "เมื่อใดที่คุณใช้ไวยากรณ์ตัวแทนแทนไวยากรณ์แลมบ์ดานิพจน์" หรือคุณหมายถึง "เมื่อใดที่คุณใช้ตัวแทนแทนคลาส / อินเทอร์เฟซ / วิธีเสมือน / ฯลฯ "
Niki

คำตอบ:


100

ตอนนี้เรามีนิพจน์แลมบ์ดาและเมธอดที่ไม่ระบุตัวตนใน C # ฉันใช้ผู้รับมอบสิทธิ์มากขึ้น ใน C # 1 ซึ่งคุณมักจะต้องมีวิธีการแยกต่างหากเพื่อใช้ตรรกะการใช้ตัวแทนมักไม่สมเหตุสมผล ทุกวันนี้ฉันใช้ผู้รับมอบสิทธิ์สำหรับ:

  • ตัวจัดการเหตุการณ์ (สำหรับ GUI และอื่น ๆ )
  • เธรดเริ่มต้น
  • การโทรกลับ (เช่นสำหรับ async API)
  • LINQ และสิ่งที่คล้ายกัน (รายการค้นหา ฯลฯ )
  • ที่อื่นใดที่ฉันต้องการใช้โค้ด "เทมเพลต" อย่างมีประสิทธิภาพโดยมีตรรกะเฉพาะบางอย่างอยู่ภายใน (ซึ่งผู้รับมอบสิทธิ์ให้ความเชี่ยวชาญ)

ควรค่าแก่การกล่าวถึง "การผลักดัน" ใน Push LINQ หรือไม่?
Marc Gravell

3
ไม่แน่ใจว่าฉันจะอธิบายสั้น ๆ ได้อย่างไรโดยไม่ทำให้สิ่งต่างๆสับสนมากขึ้น :) (อาจจะครอบคลุมโดยตัวจัดการเหตุการณ์ LINQ และบิตเทมเพลตอยู่ดี!
Jon Skeet

1
ประโยคแรกของคุณไม่สมเหตุสมผลสักเท่าไหร่
senfo

3
ฉันรู้ว่าคุณกำลังพยายามจะพูดอะไร แต่ฉันจะพบว่าสิ่งต่อไปนี้อ่านง่ายขึ้น: "ตอนนี้เรามีนิพจน์แลมบ์ดาและวิธีการไม่ระบุตัวตนใน C # ฉันใช้ผู้รับมอบสิทธิ์มากกว่า" ฉันรู้ว่าฉันกำลังคิดสั้น แต่ฉันต้องอ่านประโยคนั้นสองสามครั้งก่อนที่มันจะสมเหตุสมผลสำหรับฉัน
senfo

4
+1 สำหรับความกล้าที่จะท้าทาย Mr Skeet ที่เคารพ ;-)
indra

29

ผู้ได้รับมอบหมายมีประโยชน์มากสำหรับวัตถุประสงค์หลายประการ

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

นี่คือตัวอย่างโง่ ๆ - ฉันแน่ใจว่าคุณสามารถคาดเดาสิ่งที่มีประโยชน์มากกว่านี้ได้:

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<String> names = new List<String>
        {
            "Nicole Hare",
            "Michael Hare",
            "Joe Hare",
            "Sammy Hare",
            "George Washington",
        };

        // Here I am passing "inMyFamily" to the "Where" extension method
        // on my List<String>.  The C# compiler automatically creates 
        // a delegate instance for me.
        IEnumerable<String> myFamily = names.Where(inMyFamily);

        foreach (String name in myFamily)
            Console.WriteLine(name);
    }

    static Boolean inMyFamily(String name)
    {
        return name.EndsWith("Hare");
    }
}

11
static Boolean inMyFamily(String name)วิธีการเป็นตัวแทน ที่ไหนรับผู้รับมอบสิทธิ์เป็นพารามิเตอร์ เนื่องจากผู้รับมอบสิทธิ์เป็นเพียงตัวชี้ฟังก์ชันเมื่อคุณส่งชื่อเมธอดเข้าไปในชื่อ.Where(delegate)ที่จะกลายเป็นผู้รับมอบสิทธิ์ เนื่องจาก inMyFamily ส่งคืนประเภทบูลีนจึงถือว่าเป็นเพรดิเคต เพรดิเคตเป็นเพียงผู้รับมอบสิทธิ์ที่ส่งคืนบูลีน
Landon Poch

4
"เพรดิเคตเป็นเพียงผู้รับมอบสิทธิ์ที่ส่งคืนบูลีน" +1
daehaai

@LandonPoch ความคิดเห็นนั้นน่าจะอยู่ในคำตอบได้ดีกว่า ฉันเป็นมือใหม่ไม่สามารถระบุได้ว่ามันคือที่ไหน ขอบคุณ.
Eakan Gopalakrishnan

@Eakan ฉันไม่ได้ตอบคำถามหลักจริงๆ (คุณจะใช้ผู้รับมอบสิทธิ์เมื่อใด) ดังนั้นฉันจึงทิ้งไว้เป็นความคิดเห็นแทน
Landon Poch

14

พบอีกคำตอบที่น่าสนใจ:

เพื่อนร่วมงานเพิ่งถามฉันคำถามนี้ - อะไรคือจุดที่ผู้ได้รับมอบหมายใน. NET? คำตอบของฉันสั้นมากและเป็นคำตอบที่เขาไม่พบทางออนไลน์: เพื่อชะลอการดำเนินการของวิธีการ

ที่มา: LosTechies

เช่นเดียวกับที่ LINQ กำลังทำอยู่


+1 .. ไม่ได้คิดแบบนั้น จุดดี
ลูกา 101

12

คุณสามารถใช้ผู้รับมอบสิทธิ์เพื่อประกาศตัวแปรและพารามิเตอร์ประเภทฟังก์ชัน

ตัวอย่าง

พิจารณารูปแบบ "การยืมทรัพยากร" คุณต้องการควบคุมการสร้างและล้างทรัพยากรในขณะที่อนุญาตให้รหัสไคลเอ็นต์ "ยืม" ทรัพยากรระหว่างกัน

นี่เป็นการประกาศประเภทผู้รับมอบสิทธิ์

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

วิธีการใด ๆ ที่ตรงกับลายเซ็นนี้สามารถใช้เพื่อสร้างอินสแตนซ์ผู้แทนประเภทนี้ได้ ใน C # 2.0 สามารถทำได้โดยปริยายเพียงแค่ใช้ชื่อเมธอดและใช้เมธอดที่ไม่ระบุตัวตน

วิธีนี้ใช้ประเภทเป็นพารามิเตอร์ สังเกตการร้องขอของผู้รับมอบสิทธิ์

public class DataProvider
{
    protected string _connectionString;

    public DataProvider( string psConnectionString )
    {
        _connectionString = psConnectionString;
    }

    public void UseReader( string psSELECT, DataReaderUser readerUser )
    {
        using ( SqlConnection connection = new SqlConnection( _connectionString ) )
        try
        {
            SqlCommand command = new SqlCommand( psSELECT, connection );
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();

            while ( reader.Read() )
                readerUser( reader );  // the delegate is invoked
        }
        catch ( System.Exception ex )
        {
            // handle exception
            throw ex;
        }
    }
}

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

string sTableName = "test";
string sQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='" + sTableName + "'";

DataProvider.UseReader( sQuery,
    delegate( System.Data.IDataReader reader )
    {
        Console.WriteLine( sTableName + "." + reader[0] );
    } );

11

ผู้ร่วมประชุมมักจะใช้แทนอินเทอร์เฟซด้วยวิธีการเดียวตัวอย่างทั่วไปของสิ่งนี้คือรูปแบบผู้สังเกตการณ์ ในภาษาอื่น ๆ หากคุณต้องการรับการแจ้งเตือนว่ามีบางอย่างเกิดขึ้นคุณอาจกำหนดสิ่งต่างๆเช่น:

class IObserver{ void Notify(...); }

ใน C # มักจะแสดงออกโดยใช้เหตุการณ์โดยที่ตัวจัดการคือผู้รับมอบสิทธิ์ตัวอย่างเช่น:

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

อีกหนึ่งสถานที่ที่ดีในการใช้ผู้รับมอบสิทธิ์หากคุณต้องส่งเพรดิเคตลงในฟังก์ชันตัวอย่างเช่นเมื่อเลือกชุดของรายการจากรายการ:

myList.Where(i => i > 10);

ข้างต้นเป็นตัวอย่างของไวยากรณ์แลมบ์ดาซึ่งสามารถเขียนได้ดังนี้:

myList.Where(delegate(int i){ return i > 10; });

สถานที่อื่นที่สามารถใช้ตัวแทนได้อย่างมีประโยชน์คือการลงทะเบียนฟังก์ชั่นของโรงงานเช่น:

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

ฉันหวังว่านี่จะช่วยได้!


ตัวอย่างที่ดีพร้อมไวยากรณ์ .. ขอบคุณ .. :)
Raghu

10

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

NoDelegates.cs

using System;

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Test.checkInt(1);
        Test.checkMax(1);
        Test.checkMin(1);

        Test.checkInt(10);
        Test.checkMax(10);
        Test.checkMin(10);

        Test.checkInt(20);
        Test.checkMax(20);
        Test.checkMin(20);

        Test.checkInt(30);
        Test.checkMax(30);
        Test.checkMin(30);

        Test.checkInt(254);
        Test.checkMax(254);
        Test.checkMin(254);

        Test.checkInt(255);
        Test.checkMax(255);
        Test.checkMin(255);

        Test.checkInt(256);
        Test.checkMax(256);
        Test.checkMin(256);
    }
}

Delegates.cs

using System;

public delegate void Valid(int a);

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Valid v1 = new Valid(Test.checkInt);
        v1 += new Valid(Test.checkMax);
        v1 += new Valid(Test.checkMin);
        v1(1);
        v1(10);
        v1(20);
        v1(30);
        v1(254);
        v1(255);
        v1(256);
    }
}

5

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

ด้วยExpressionคุณยังสามารถทำเช่นเดียวกันในการสร้างรหัสในการบิน - ตัวอย่างเช่นคุณสามารถสร้างExpressionที่แสดงถึงผู้ประกอบการ + สำหรับประเภทได้รับการแต่งตั้งที่รันไทม์ (เพื่อให้การสนับสนุนผู้ประกอบการสำหรับ generics, ซึ่งเป็นภาษาที่ไม่ได้ให้) ; และคุณสามารถรวบรวมExpressionไปยังผู้รับมอบสิทธิ์ที่พิมพ์ - งานเสร็จแล้ว


5

ผู้ร่วมประชุมจะถูกใช้ทุกครั้งที่คุณใช้กิจกรรมนั่นคือกลไกที่พวกเขาทำงาน

นอกจากนี้ผู้รับมอบสิทธิ์ยังมีประโยชน์อย่างมากสำหรับสิ่งต่างๆเช่นการใช้คำค้นหา LINQ ตัวอย่างเช่นการสืบค้น LINQ จำนวนมากใช้ตัวแทน (บ่อยครั้งFunc<T,TResult>) ซึ่งสามารถใช้สำหรับการกรอง



2

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


1

ฉันใช้ผู้รับมอบสิทธิ์เพื่อสื่อสารกับเธรด

ตัวอย่างเช่นฉันอาจมีแอป win form ที่ดาวน์โหลดไฟล์ แอปจะเริ่มเธรดผู้ปฏิบัติงานเพื่อทำการดาวน์โหลด (ซึ่งป้องกันไม่ให้ GUI ล็อก) เธรดผู้ปฏิบัติงานใช้ผู้รับมอบสิทธิ์ในการส่งข้อความสถานะ (เช่นความคืบหน้าในการดาวน์โหลด) กลับไปที่โปรแกรมหลักเพื่อให้ GUI สามารถอัปเดตแถบสถานะได้



0

บรรทัดแรกของการใช้งานคือการแทนที่รูปแบบ Observer / Observable (เหตุการณ์) ประการที่สองรูปแบบกลยุทธ์ที่สวยงามสวยงาม สามารถรวบรวมประเพณีอื่น ๆ ได้มากมายแม้ว่าจะลึกลับกว่าสองข้อแรกที่ฉันคิด


0

เหตุการณ์การดำเนินการอื่น ๆ


0

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


0

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

storage.Store(conditions, Download(item))

อย่างไรก็ตามด้วยผู้รับมอบสิทธิ์ (lambdas ที่แม่นยำยิ่งขึ้น) คุณสามารถทำสิ่งต่อไปนี้ได้โดยเปลี่ยนลายเซ็นของร้านค้าเพื่อให้ได้รับเงื่อนไขและ Func <Item, DownloadedObject> และใช้ในลักษณะนี้:

storage.Store(conditions, (item) => Download(item))

ดังนั้นพื้นที่เก็บข้อมูลจะประเมินผู้รับมอบสิทธิ์ในกรณีที่จำเป็นเท่านั้นการดำเนินการดาวน์โหลดขึ้นอยู่กับเงื่อนไข


จุดรอง แต่ "แม่นยำกว่า lambdas" - คุณสามารถทำได้เช่นเดียวกันกับวิธีการที่ไม่ระบุตัวตนใน C # 2.0 แม้ว่ามันจะดูละเอียดกว่า: delegate (ItemType item) {[return] Download (item);}
Marc Gravell

แน่นอนเช่นเดียวกับ LINQ: lambdas ไม่มีอะไรมากไปกว่าน้ำตาลซินแทติกสำหรับผู้ร่วมประชุม พวกเขาทำให้ผู้ได้รับมอบหมายสามารถเข้าถึงได้มากขึ้น
Santiago Palladino

Lambdas เป็นมากกว่าผู้ได้รับมอบหมายเล็กน้อยเนื่องจากสามารถเปลี่ยนเป็นต้นไม้แสดงออกได้เช่นเดียวกับผู้รับมอบสิทธิ์
Jon Skeet

นอกจากนี้ lambdas ยังสามารถรวบรวมเป็นนิพจน์ซึ่งแตกต่างจากผู้รับมอบสิทธิ์อย่างสิ้นเชิง แต่ตัวอย่างของคุณใช้ Func <,> ซึ่งสามารถใช้ได้จาก anon-method นิพจน์จะเจ็บปวดอย่างยิ่งที่ต้องเขียนใน C # 2.0
Marc Gravell


0

พารามิเตอร์เปรียบเทียบในอาร์เรย์ In Array.Sort (T [], การเปรียบเทียบการเปรียบเทียบ), List.Sort (การเปรียบเทียบการเปรียบเทียบ) ฯลฯ


0

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


0

Delegate ใช้เพื่อเรียกวิธีการโดยอ้างอิง ตัวอย่างเช่น:

  delegate void del_(int no1,int no2);
class Math
{
   public static void add(int x,int y)
   {
     Console.WriteLine(x+y);
   }
   public static void sub(int x,int y)
   {
     Console.WriteLine(x-y);
   }
}



    class Program
    {
        static void Main(string[] args)
        {
            del_ d1 = new del_(Math.add);
            d1(10, 20);
            del_ d2 = new del_(Math.sub);
            d2(20, 10);
            Console.ReadKey();
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.