【问题标题】:When would you use delegates in C#? [closed]什么时候在 C# 中使用委托? [关闭]
【发布时间】:2008-10-10 13:02:06
【问题描述】:

您在 C# 中如何使用委托?

【问题讨论】:

标签: c# .net delegates


【解决方案1】:

委托通常可以用一种方法代替接口,一个常见的例子就是观察者模式。在其他语言中,如果您想收到发生某事的通知,您可以定义如下内容:

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

我希望这会有所帮助!

【讨论】:

  • 语法很好的例子.. 谢谢.. :)
【解决方案2】:

找到另一个有趣的答案:

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

来源:LosTechies

就像 LINQ 正在做的那样。

【讨论】:

  • +1..没想到那样。好点
【解决方案3】:

委托对许多用途都非常有用。

其中一个目的是使用它们来过滤数据序列。在这种情况下,您将使用一个谓词委托,它接受一个参数并根据委托本身的实现返回 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");
    }
}

【讨论】:

  • static Boolean inMyFamily(String name) 方法是委托。 Where 将委托作为参数。因为当您将方法名称传递给成为委托的.Where(delegate) 时,委托只是函数指针。由于 inMyFamily 返回一个布尔类型,它实际上被认为是一个谓词。谓词只是返回布尔值的委托。
  • "谓词只是返回布尔值的委托。" +1
  • @LandonPoch 该评论会更好地放在答案中。我作为初学者无法弄清楚它在哪里。谢谢。
  • @Eakan,我并没有真正回答主要问题(你什么时候使用代表),所以我把它作为评论留下了。
【解决方案4】:

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

  • 事件处理程序(用于 GUI 等)
  • 开始线程
  • 回调(例如异步 API)
  • LINQ 和类似(List.Find 等)
  • 我想在其他任何地方有效地应用带有一些专门逻辑的“模板”代码(委托提供专门化的地方)

【讨论】:

  • Push LINQ 中的“push”值得一提吗?
  • 不知道如何简单地解释它而不让事情变得更混乱:)(可以说它被事件处理程序、LINQ 和模板位所覆盖!
  • 你的第一句话没有多大意义。
  • 我知道你想说什么,但我会发现以下内容更容易阅读:“现在我们在 C# 中拥有 lambda 表达式和匿名方法,我更多地使用委托。”我知道我在吹毛求疵,但我真的必须把这句话读几遍才能理解。
  • +1 表示敢于挑战可敬的斯基特先生 ;-)
【解决方案5】:

您可以使用委托来声明函数类型的变量和参数。

示例

考虑“资源借用”模式。您希望控制资源的创建和清理,同时允许客户端代码在两者之间“借用”资源。

这声明了一个委托类型。

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

【讨论】:

    【解决方案6】:

    我来晚了,但我今天无法弄清楚代表的目的,并编写了两个简单的程序,它们给出的输出相同,我认为很好地解释了它们的目的。

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

    【讨论】:

      【解决方案7】:

      稍微不同的用途是加速反射;即不是每次都使用反射,您可以使用Delegate.CreateDelegate 为方法(MethodInfo)创建一个(类型化的)委托,然后调用该委托。这样每次调用会快得多,因为检查已经完成。

      使用Expression,您还可以执行相同的操作来动态创建代码 - 例如,您可以轻松创建一个Expression,它代表在运行时选择的类型的 + 运算符(为泛型提供运算符支持,该语言不提供);并且您可以将Expression 编译为类型化委托 - 工作完成。

      【讨论】:

        【解决方案8】:

        在您使用事件的任何时候都会使用委托 - 这就是它们的工作机制。

        此外,委托对于诸如使用 LINQ 查询之类的事情非常有用。例如,许多 LINQ 查询采用可用于过滤的委托(通常为 Func&lt;T,TResult&gt;)。

        【讨论】:

          【解决方案9】:

          为事件订阅事件处理程序

          【讨论】:

            【解决方案10】:

            例如here。您有一种方法来处理满足某些要求的对象。但是,您希望能够以多种方式处理对象。不必创建单独的方法,您可以简单地将处理对象的匹配方法分配给委托,并将委托传递给选择对象的方法。这样,您可以将不同的方法分配给一个选择器方法。我试图让这很容易理解。

            【讨论】:

              【解决方案11】:

              我使用委托与线程通信。

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

              【讨论】:

                【解决方案12】:
                1. 用于事件处理程序

                2. 在方法参数中传递方法

                【讨论】:

                  【解决方案13】:

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

                  【讨论】:

                    【解决方案14】:

                    事件,其他anynch操作

                    【讨论】:

                      【解决方案15】:

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

                      【讨论】:

                        【解决方案16】:

                        延迟参数初始化!除了所有先前的答案(策略模式、观察者模式等)之外,委托还允许您处理参数的延迟初始化。例如,假设您有一个函数 Download(),它需要相当多的时间并返回某个 DownloadedObject。此对象由存储根据特定条件消耗。通常,您会:

                        storage.Store(conditions, Download(item))
                        

                        但是,对于委托(更准确地说,lambdas),您可以通过更改 store 的签名来执行以下操作,以便它接收 Condition 和 Func 并像这样使用它:

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

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

                        【讨论】:

                        • 次要问题,但“更准确地说,lambdas” - 你可以在 C# 2.0 中使用匿名方法做同样的事情,尽管它会更冗长:delegate (ItemType item) { [return] 下载(项目);}
                        • 当然,和 LINQ 一样:lambda 只不过是代表的语法糖。他们只是让代表更容易接近。
                        • Lambda 不仅仅是委托,因为它们可以转换为表达式树以及委托。
                        • 嗯,lambdas 也可以编译为表达式,这与委托完全不同。但是您的示例使用了 Func,它可以从匿名方法中使用。用 C# 2.0 编写表达式会非常痛苦。
                        【解决方案17】:

                        委托的使用

                        1. 事件处理
                        2. 多播

                        【讨论】:

                          【解决方案18】:

                          In Array.Sort(T[] array,Comparison comparison)、List.Sort(Comparison comparison)等中的比较参数

                          【讨论】:

                            【解决方案19】:

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

                            【讨论】:

                              【解决方案20】:

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

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

                              【讨论】:

                                猜你喜欢
                                • 2016-04-19
                                • 2011-06-09
                                • 2014-04-06
                                • 1970-01-01
                                • 1970-01-01
                                • 2015-05-26
                                • 1970-01-01
                                • 2010-09-20
                                • 2012-09-19
                                相关资源
                                最近更新 更多