什么时候在C#中使用委托?[关闭]


101

您在C#中使用委托的方式是什么?


2
您是指.NET类型系统中的委托还是C#委托语法?您是说“何时使用委托语法而不是lambda表达式语法”还是“何时使用委托而不是类/接口/虚拟方法/等”?
Niki

Answers:


100

现在,我们在C#中有了lambda表达式和匿名方法,我将使用委托更多。在C#1中,您总是必须有一个单独的方法来实现逻辑,而使用委托通常是没有意义的。这些天,我使用委托进行以下工作:

  • 事件处理程序(适用于GUI等)
  • 启动线程
  • 回调(例如异步API)
  • LINQ和类似的东西(List.Find等)
  • 我想在内部使用一些特殊逻辑有效地应用“模板”代码的其他任何地方(委托人提供特殊化)

值得一提的是Push LINQ中的“推”功能吗?
马克·格雷韦尔

3
不知道如何简短地解释它而不会使事情变得更加令人困惑:)(可以说它已经被事件处理程序,LINQ和模板所覆盖了!)
Jon Skeet

1
您的第一句话没有多大意义。
SENFO

3
我知道您要说的是什么,但是我发现以下内容更容易理解:“现在,在C#中我们有了lambda表达式和匿名方法,我使用委托的方式更多。” 我知道我很挑剔,但是我真的不得不读过那句话,才对我有意义。
SENFO 2009年

4
+1敢于挑战古老的Skeet先生;-)
indra 2010年

29

代表对于许多目的非常有用。

这样的目的之一是将它们用于过滤数据序列。在这种情况下,您将使用谓词委托,该谓词接受一个参数并根据委托本身的实现返回true或false。

这是一个愚蠢的例子-我相信您可以从中推断出一些更有用的东西:

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返回布尔类型,因此实际上将其视为谓词。谓词只是返回布尔值的委托。
兰登·波奇

4
“谓词只是返回布尔值的委托。” +1
daehaai

@LandonPoch可以将评论更好地放在答案中。我是一个初学者,无法确定它在哪里。谢谢。
Eakan Gopalakrishnan 2014年

@Eakan,我并没有真正回答主要问题(何时使用委托人),所以我将其留为评论。
Landon Poch 2014年

14

找到了另一个有趣的答案:

一位同事刚刚问我这个问题-.NET中的代表有什么意义?我的回答很短,而且他在网上找不到:延迟执行一种方法。

资料来源:LosTechies

就像LINQ所做的一样。


+1 ..并没有这样考虑。好点
Luke101

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

上面是lambda语法的示例,也可以编写如下:

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代表运行时选择的类型的+运算符(以提供对语言不提供的泛型的运算符支持) ; 您可以将编译Expression为类型委托-完成工作。


5

每次使用事件时都会使用代表-这就是它们工作的机制。

此外,委托对于诸如使用LINQ查询之类的事情非常有用。例如,许多LINQ查询使用一个委托(通常为Func<T,TResult>),该委托可以用于过滤。



2

一个例子可能在这里看到。您有一种方法来处理满足某些要求的对象。但是,您希望能够以多种方式处理对象。您不必创建单独的方法,只需分配一个将对象处理为对象的匹配方法,然后将该委托传递给选择对象的方法。这样,您可以将不同的方法分配给一个选择器方法。我试图使其易于理解。


1

我使用委托与线程进行通信。

例如,我可能有一个可下载文件的Win Forms应用程序。该应用程序启动一个工作线程进行下载(这可以防止GUI锁定)。工作线程使用委托将状态消息(例如下载进度)发送回主程序,以便GUI可以更新状态栏。



0

使用的第一行是替换Observer / Observable(事件)模式。第二个是策略模式的精美版本。可以收集其他各种用法,尽管比我认为的前两种更为深奥。


0

活动,其他Anynch操作


0

任何时候您都想封装行为,但以统一的方式调用它。事件处理程序,回调函数等。您可以使用接口和强制类型转换来完成类似的操作,但有时行为不一定与类型对象相关。有时您只是需要封装行为。


0

惰性参数初始化!除了所有先前的答案(策略模式,观察者模式等)之外,委托还允许您处理参数的延迟初始化。例如,假设您有一个函数Download(),该函数需要花费大量时间并返回某个DownloadedObject。存储对象将根据特定条件使用此对象。通常,您将:

storage.Store(conditions, Download(item))

但是,对于委托人(更确切地说是lambdas),您可以通过更改存储区的签名来执行以下操作,以使其接收到Condition和Func <Item,DownloadedObject>并像这样使用它:

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

因此,存储将仅在必要时评估委托,并根据条件执行下载。


小调点,但再“更准确地说,lambda表达式” -你可以做同样的在C#2.0匿名方法,但它会更详细:委托(ItemType的项目){[返回]下载(项目);}
马克·Gravell

当然,与LINQ相同:lambda只是代表的语法糖。他们只是使代表更容易访问。
圣地亚哥帕拉迪诺

Lambda不仅仅是委托,还可以转换为表达式树和委托。
乔恩·斯基特

好吧,lambda也可以编译为与委托完全不同的表达式。但是您的示例使用了Func <,>,可以从匿名方法中使用它。使用C#2.0编写表达式将非常痛苦。
马克·格雷韦尔



0

据我所知,委托可以转换为函数指针。与带有函数指针的本机代码进行互操作时,这使生活变得更加轻松,因为即使原始程序员没有为此做任何准备,它们也可以有效地面向对象。


0

委托用于通过其引用来调用方法。例如:

  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();
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.