Khi nào bạn sử dụng các đại diện trong C #? [đóng cửa]


101

Bạn sử dụng các đại biểu trong C # là gì?


2
Ý của bạn là đại biểu trong hệ thống kiểu .NET hay cú pháp đại biểu C #? Ý của bạn là "khi nào bạn sử dụng cú pháp đại biểu thay vì cú pháp biểu thức lambda" hay ý bạn là "khi nào bạn sử dụng đại biểu thay vì các lớp / giao diện / phương thức ảo / vv."?
Niki

Câu trả lời:


100

Bây giờ chúng ta có các biểu thức lambda và các phương thức ẩn danh trong C #, tôi sử dụng các đại biểu nhiều hơn nữa. Trong C # 1, nơi bạn luôn phải có một phương pháp riêng để triển khai logic, việc sử dụng một ủy nhiệm thường không có ý nghĩa. Ngày nay, tôi sử dụng đại biểu cho:

  • Trình xử lý sự kiện (dành cho GUI và hơn thế nữa)
  • Bắt đầu chuỗi
  • Gọi lại (ví dụ: đối với API không đồng bộ)
  • LINQ và tương tự (List.Find, v.v.)
  • Bất kỳ nơi nào khác mà tôi muốn áp dụng hiệu quả mã "mẫu" với một số logic chuyên biệt bên trong (nơi người được ủy quyền cung cấp chuyên môn)

Đáng nói đến "cú hích" trong Push LINQ?
Marc Gravell

3
Bạn không chắc chắn làm thế nào tôi muốn giải thích nó một thời gian ngắn mà không làm nhiều điều khó hiểu :) (Có thể cho rằng nó được bao phủ bởi xử lý sự kiện, LINQ, và templaty cắn anyway!
Jon Skeet

1
Câu đầu tiên của bạn không có nhiều ý nghĩa.
senfo 19/02/09

3
Tôi biết bạn đang muốn nói gì, nhưng tôi sẽ thấy điều sau dễ đọc hơn: "Bây giờ chúng ta có các biểu thức lambda và các phương thức ẩn danh trong C #, tôi sử dụng các đại biểu nhiều hơn nữa." Tôi biết mình đang khó chịu, nhưng tôi thực sự đã phải đọc câu đó vài lần trước khi nó có ý nghĩa với tôi.
senfo 19/02/09

4
+1 vì dám thách thức Mr Skeet đáng kính ;-)
indra

29

Các đại biểu rất hữu ích cho nhiều mục đích.

Một trong những mục đích như vậy là sử dụng chúng để lọc chuỗi dữ liệu. Trong trường hợp này, bạn sẽ sử dụng một đại biểu vị từ chấp nhận một đối số và trả về true hoặc false tùy thuộc vào việc triển khai chính ủy nhiệm đó.

Đây là một ví dụ ngớ ngẩn - tôi chắc chắn rằng bạn có thể ngoại suy điều gì đó hữu ích hơn từ điều này:

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
Các static Boolean inMyFamily(String name)phương pháp là các đại biểu. Trong đó lấy một đại biểu làm tham số. Vì đại biểu chỉ là con trỏ hàm khi bạn chuyển tên phương thức vào phương thức .Where(delegate)đó sẽ trở thành đại biểu. Vì inMyFamily trả về kiểu boolean nên nó thực sự được coi là một vị từ. Predicates chỉ là các đại biểu trả về các boolean.
Landon Poch

4
"Predicates chỉ là đại biểu trả về boolean." +1
daehaai

@LandonPoch rằng bình luận sẽ tốt hơn trong câu trả lời. tôi là một người mới bắt đầu không thể biết nó ở đâu. cảm ơn.
Eakan Gopalakrishnan

@Eakan, tôi đã không thực sự trả lời câu hỏi chính (khi nào bạn sẽ sử dụng đại biểu) vì vậy tôi đã để nó dưới dạng nhận xét.
Landon Poch

14

Tìm thấy một câu trả lời thú vị khác:

Một đồng nghiệp vừa hỏi tôi câu hỏi này - điểm của các đại biểu trong .NET là gì? Câu trả lời của tôi rất ngắn gọn và một câu mà anh ấy chưa tìm thấy trên mạng: trì hoãn việc thực thi một phương pháp.

Nguồn: LosTechies

Giống như LINQ đang làm.


+ 1..không nghĩ về nó như vậy. Điểm tốt
Luke101

12

Bạn có thể sử dụng các đại diện để khai báo các biến và tham số kiểu hàm.

Thí dụ

Hãy xem xét mô hình "vay tài nguyên". Bạn muốn kiểm soát việc tạo và dọn dẹp tài nguyên, đồng thời cho phép mã máy khách "mượn" tài nguyên ở giữa.

Điều này khai báo một kiểu đại biểu.

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

Bất kỳ phương thức nào khớp với chữ ký này đều có thể được sử dụng để khởi tạo một đại biểu kiểu này. Trong C # 2.0, điều này có thể được thực hiện ngầm, đơn giản bằng cách sử dụng tên của phương thức, cũng như bằng cách sử dụng các phương thức ẩn danh.

Phương thức này sử dụng kiểu làm tham số. Lưu ý lời kêu gọi của đại biểu.

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

Hàm có thể được gọi với một phương thức ẩn danh như sau. Lưu ý rằng phương thức ẩn danh có thể sử dụng các biến được khai báo bên ngoài chính nó. Điều này cực kỳ tiện dụng (mặc dù ví dụ có một chút giả thiết).

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

Các đại biểu thường có thể được sử dụng thay cho một giao diện với một phương thức, một ví dụ phổ biến về điều này sẽ là mẫu quan sát. Trong các ngôn ngữ khác, nếu bạn muốn nhận được thông báo rằng điều gì đó đã xảy ra, bạn có thể xác định điều gì đó như:

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

Trong C #, điều này thường được thể hiện nhiều hơn bằng cách sử dụng các sự kiện, trong đó trình xử lý là một đại biểu, ví dụ:

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

Một nơi tuyệt vời khác để sử dụng đại biểu nếu khi bạn phải chuyển một vị từ vào một hàm, ví dụ: khi chọn một tập hợp các mục từ danh sách:

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

Trên đây là một ví dụ về cú pháp lambda, cũng có thể được viết như sau:

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

Một nơi khác có thể hữu ích để sử dụng các đại diện là đăng ký các chức năng của nhà máy, ví dụ:

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

Tôi hi vọng cái này giúp được!


Ví dụ tốt đẹp với cú pháp .. Cảm ơn .. :)
Raghu

10

Tôi đến đây thực sự muộn nhưng tôi gặp khó khăn trong việc tìm ra mục đích của các đại biểu ngày hôm nay và đã viết hai chương trình đơn giản đưa ra cùng một kết quả mà tôi nghĩ giải thích tốt mục đích của họ.

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

Một cách sử dụng hơi khác là tăng tốc độ phản chiếu; tức là thay vì sử dụng phản chiếu mỗi lần, bạn có thể sử dụng Delegate.CreateDelegateđể tạo một đại biểu (đã nhập) cho một phương thức (a MethodInfo) và gọi đại biểu đó thay thế. Sau đó, việc này sẽ nhanh hơn nhiều cho mỗi cuộc gọi, vì việc kiểm tra đã được thực hiện.

Với Expression, bạn cũng có thể làm điều tương tự để tạo mã khi đang di chuyển - ví dụ: bạn có thể dễ dàng tạo một Expressionđại diện cho toán tử + cho một loại được chọn trong thời gian chạy (để cung cấp hỗ trợ toán tử cho các generic mà ngôn ngữ này không cung cấp) ; và bạn có thể biên dịch một Expressionđại biểu đã nhập - công việc đã hoàn thành.


5

Các đại biểu được sử dụng bất cứ khi nào bạn sử dụng các sự kiện - đó là cơ chế hoạt động của chúng.

Ngoài ra, các đại biểu rất hữu ích cho những việc như sử dụng các truy vấn LINQ. Ví dụ: nhiều truy vấn LINQ lấy một đại biểu (thường Func<T,TResult>) có thể được sử dụng để lọc.


4

đăng ký người xử lý sự kiện cho các sự kiện


2

Một ví dụ có thể được thấy ở đây . Bạn có một phương pháp để xử lý một đối tượng đáp ứng các yêu cầu nhất định. Tuy nhiên, bạn muốn có thể xử lý đối tượng theo nhiều cách. Thay vì phải tạo các phương thức riêng biệt, bạn có thể chỉ cần gán một phương thức so khớp xử lý đối tượng cho một đại biểu và chuyển đại biểu cho phương thức chọn các đối tượng. Bằng cách đó, bạn có thể gán các phương thức khác nhau cho một phương thức bộ chọn. Tôi đã cố gắng làm cho điều này dễ hiểu.


1

Tôi sử dụng các đại biểu để giao tiếp với các chủ đề.

Ví dụ: tôi có thể có một ứng dụng win form tải xuống một tệp. Ứng dụng bắt đầu một chuỗi công nhân để thực hiện tải xuống (điều này ngăn GUI bị khóa). Luồng công nhân sử dụng các đại biểu để gửi thông báo trạng thái (ví dụ: tiến trình tải xuống) trở lại chương trình chính, để GUI có thể cập nhật thanh trạng thái.


0
  1. Đối với trình xử lý sự kiện

  2. Để truyền phương thức trong một tham số phương thức


0

Dòng đầu tiên sử dụng là thay thế mẫu Observer / Observable (sự kiện). Thứ hai, một phiên bản trang nhã đẹp mắt của mô hình Chiến lược. Nhiều cách sử dụng khác có thể được thu thập, mặc dù bí truyền hơn hai cách đầu tiên tôi nghĩ.


0

Sự kiện, hoạt động bất kỳ khác


0

Bất cứ lúc nào bạn muốn đóng gói hành vi, nhưng gọi nó theo một cách thống nhất. Trình xử lý sự kiện, chức năng gọi lại, v.v. Bạn có thể thực hiện những việc tương tự bằng cách sử dụng Giao diện và phôi, nhưng đôi khi, hành vi không nhất thiết phải gắn với một kiểu hoặc đối tượng . Đôi khi bạn chỉ có hành vi mà bạn cần gói gọn.


0

Khởi tạo tham số lười biếng! Bên cạnh tất cả các câu trả lời trước đó (mẫu chiến lược, mẫu người quan sát, v.v.), các đại biểu cho phép bạn xử lý việc khởi tạo tham số lười biếng. Ví dụ: giả sử bạn có một hàm Download () mất khá nhiều thời gian và trả về một DownloadedObject nhất định. Đối tượng này được tiêu thụ bởi một Kho lưu trữ tùy thuộc vào một Điều kiện nhất định. Thông thường, bạn sẽ:

storage.Store(conditions, Download(item))

Tuy nhiên, với các ủy quyền (chính xác hơn là lambdas), bạn có thể làm như sau, bằng cách thay đổi chữ ký của cửa hàng để nó nhận được Điều kiện và Hàm <Item, DownloadedObject> và sử dụng nó như sau:

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

Do đó, bộ nhớ sẽ chỉ đánh giá ủy nhiệm nếu cần thiết, thực hiện tải xuống tùy thuộc vào Điều kiện.


Điểm nhỏ, nhưng "chính xác hơn là lambdas" - bạn có thể làm tương tự với một phương thức ẩn danh trong C # 2.0, mặc dù nó sẽ dài dòng hơn: ủy quyền (ItemType item) {[return] Download (item);}
Marc Gravell

Chắc chắn, giống như LINQ: lambdas không khác gì đường cú pháp cho các đại biểu. Họ chỉ làm cho các đại biểu dễ tiếp cận hơn.
Santiago Palladino

Lambdas không chỉ đơn thuần là các đại biểu, vì chúng có thể chuyển đổi thành cây biểu hiện cũng như các đại biểu.
Jon Skeet

Chà, lambdas cũng có thể được biên dịch thành Biểu thức, hoàn toàn khác với các đại biểu. Nhưng ví dụ của bạn đã sử dụng Func <,>, có thể được sử dụng từ anon-method. Các biểu thức sẽ rất khó để viết trong C # 2.0.
Marc Gravell


0

Tham số so sánh trong In Array.Sort (T [] mảng, So sánh so sánh), List.Sort (So sánh so sánh), v.v.


0

Theo như tôi biết, các đại biểu có thể được chuyển đổi thành các con trỏ hàm. Điều này làm cho cuộc sống dễ dàng hơn RẤT NHIỀU khi tương tác với mã gốc sử dụng con trỏ hàm, vì chúng có thể được định hướng đối tượng một cách hiệu quả, mặc dù lập trình viên ban đầu không đưa ra bất kỳ điều khoản nào để điều đó xảy ra.


0

Delegate được sử dụng để gọi một phương thức bằng tham chiếu của nó. Ví dụ:

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