【问题标题】:C# Delegates Real World Usage [duplicate]C#代表真实世界的使用[重复]
【发布时间】:2010-12-07 02:14:35
【问题描述】:

我之前问过一个关于 Delegates 的问题,是否有人有必须使用委托的场景?这对我的 C# 代码有何改进?

正如我在许多场景中使用它一样,我似乎总是能够围绕它进行编程。

【问题讨论】:

  • 事件由代表实现。你不使用事件吗?

标签: c# .net asp.net


【解决方案1】:

无论何时使用Strategy Pattern 或观察者模式,委托都比使用接口更容易。

【讨论】:

  • 那是主观的。就我个人而言,我喜欢在实现 Strategy Pattern 时使用接口。我认为它允许更大的灵活性。例如,从另一个程序集的不同类中导入行为。
  • @Stan:委托没有理由不能指向不同程序集中不同类的方法。委托与接口没有什么不同,就像接口一样,它定义了一个契约。在您只需要一个函数调用的情况下,我会说委托比接口更有吸引力。它的实现门槛较低,并且同样易于使用(也许给定 lambdas 更容易。)
【解决方案2】:

没有人提到这一点,但是如果您将 LINQ 与 Lambda 一起使用,您将一直使用匿名方法。从技术上讲,它们仍然是代表

假设您有一个名为 Person 的类

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

你想实现一个 find 方法,你可以根据他们的名字找到这个人

public Person Where(List<Person> list, string firstName)
{
   //find the string
   foreach(Person item in list)
      if(item.FirstName.Equals(firstName))
        return item;
}

这是一个非常具体的搜索,不是很动态,这意味着如果您想按姓氏搜索,您必须更改此方法或编写一个新方法。

幸运的是,LINQ 提供了一个名为 Where 的扩展方法,您需要将 delegate 传递给该方法,您可以在匿名方法的帮助下动态创建该方法。 p>

例如

string searchString = "Stan";
list.Where( person => person.FirstName.Equals(searchString));

但如果您想更改为按姓氏搜索,您只需这样做

string searchString = "R";
list.Where( person => person.LastName.Equals(searchString));

这个例子可能不是你想要的,但我只是想表明,有时我们一直在使用委托而没有考虑或意识到这一点。

【讨论】:

    【解决方案3】:

    假设您不是在谈论事件 - 当然您可以围绕它进行编程。关键是让它更好更干净

    protected void Sort()
    {
        foreach (string key in _dBase.Keys)
        {
          Array.Sort<Pair<string, Dictionary<string, T>>>(_dBase[key], 
                new Comparison<Pair<string, Dictionary<string, T>>>(
            delegate(Pair<string, Dictionary<string, T>> a, Pair<string, Dictionary<string, T>> b)
            {
                if (a == null && b != null)
                    return 1;
                else if (a != null && b == null)
                    return -1;
                else if (a == null && b == null)
                    return 0;
                else
                    return a.First.CompareTo(b.First);
            }));
        }
    }
    

    如果没有内联委托,我可以这样做吗?当然。我会在我的类中有一个仅用于这个实例的软盘私有方法吗?是的。

    编辑:如 cmets 中所述,您可以简化:

    Array.Sort<Pair<string, Dictionary<string, T>>>(_dBase[key], 
       new Comparison<Pair<string, Dictionary<string, T>>>(
       delegate(Pair<string, Dictionary<string, T>> a, Pair<string, Dictionary<string, T>> b)
       {
    

    Array.Sort<Pair<string, Dictionary<string, T>>>(_dBase[key], (a,b) =>
       {
    

    【讨论】:

    • 当您强调 nicercleaner 时,这并不是一个很好的例子
    • 特别是因为从 v2.0 和 v3.5 以来的所有其他尖括号内容都不需要编写“新比较”。 Array.Sort(_dBase[key], (a, b) => { ... });看起来好多了。
    • 数据结构有点毛茸茸的;但是没有理由提供有关如何使用或记录它的上下文。关键是要演示内联委托如何提供封装而不是使类混乱;不关注数据结构。
    【解决方案4】:

    如果您想象 C# 没有委托,您通常会遇到这样的情况,即您拥有具有一种方法的类或接口。该方法的名称是多余的。例如

    public interface IGetMail
    {
        Mail JustGetTheMail();
    }
    

    界面就是关于那个方法的。对该类型对象的引用实际上只不过是对单个可调用方法的引用。调用代码:

    Mail m = getMail.JustGetTheMail();
    

    可以简写为:

    Mail m = getMail();
    

    编译器可以毫无歧义地将其作为“语法糖”来执行,因为只有一种方法可以调用 getMail 引用。

    所以让我们将该功能添加到我们的 C# 编译器中。现在,在声明这些类型时,我们也可以让它更整洁一些。调用的时候不需要指定方法名,为什么要给方法起个名字呢?

    让我们选择一个标准方法名称,Invoke,即

    public interface IGetMail
    {
        Mail Invoke();
    }
    

    我们将添加更多语法糖,以便我们将其写为:

    public delegate Mail GetMail();
    

    嘿,转眼间。我们在 C# 编译器中添加了委托。

    (从技术上讲,CLR 也知道委托,因此 C# 编译器不会生成一个接口,而是生成一个特殊的“委托”类型,它支持异步调用,并操作一个不可变的委托列表并将它们视为一个单一的引用;但在基本形式中,它可以用接口完成。有一个建议为 Java 这样做)。

    然后我们可以更进一步并添加匿名委托 - 使它们以接口所没有的方式简洁地实现。

    所以回答你的问题 - 只要你的界面有一个方法,它就可以是一个委托,你将能够大大减少你必须编写的垃圾代码的数量。

    【讨论】:

      【解决方案5】:

      现实世界的用法:

      1. 假设您有一个名为 Checker 的简单用户控件,它仅包含 2 个复选框 - chkA 和 chkB。
      2. 在用户控件中,您可以通过实现 chkA 和 ChkB 的相应事件来控制选中/取消选中事件

      3.现在,在一个新的 Win Form 中,当您拖入 Checker...并且您的目标是确保单击 chkA 时,您必须更改标签的背景颜色...lblColorPicker。

      请注意,lblColorPicker 是一个存在于表单中的控件,并不直接绑定到 Checker

      您将如何实现这一目标?

      答案:

      1. 首先,您必须为用户控件 Checker 创建一个新事件。
      2. 要创建这个新事件,首先要做的是在您的用户控件中创建一个委托,然后将此委托用作您正在编写的新事件的定义类型。
      3. 然后必须将此事件映射到用户控件中的 chkA...事件。

      通过这种方式,您可以控制 chkA...通过任何新形式的新事件,通过引用事件...通过您刚刚编写的委托。

      所以,对于现实世界的使用来说非常重要!

      如果不编写委托,这个目标无法实现。

      如果您不这么认为......或者如果您需要更多详细说明,请告诉我。

      【讨论】:

      • 向所有人装腔作势:这里有什么办法……完全避免使用代表吗?
      • 是的,当然 - 在底部,委托只是实现具有单个方法的接口的类,事件只是观察者模式的简洁实现。要修改您的示例,只需扩展 checker 以接受 ICheckerObserver 对象并维护它们的列表。然后,在 Checker 的点击事件中,它迭代其 ICheckerObservers 列表并调用 ICheckerObserver.OnClick。
      【解决方案6】:

      匿名委托在某些情况下使代码更具可读性(您不必转到另一个方法来查看属于您的方法的代码:

      Winform 示例

      class MyForm:Form{
      //...
      protected override void OnLoad(EventArg e){
         this.Cursor=Cursors.Wait();
         this.Enabled=false; 
      
         // do a long running DB operation without blocking the UI Thread
         ThreadPool.QueueUserWorkItem(state=>{
      
           DoLongDBOperation();
      
           // re enable the form
           BeginInvoke(new Action(()=>{ this.Cursor=Cursors.Default;this.Enabled=true;}));
      
         });
      }
      

      【讨论】:

        【解决方案7】:

        如果您将事件添加到您的班级或正在做任何异步的事情(还有其他几个很好的理由拥有委托),那么委托是绝对必须的。好处是它是一种非常灵活的方法。

        【讨论】:

          【解决方案8】:

          我认为您指的是定义自定义委托?

          EventHandler 已将自定义委托定义的需求降到最低,但如果您想向方法签名添加其他参数,它们仍然很有用。

          【讨论】:

            【解决方案9】:

            当您需要修改或更改 winForms 控件的任何属性时,您需要使用委托将控制权传递回创建该控件的线程……这里仅举一个例子。

            【讨论】:

              【解决方案10】:

              一个示例是发布/订阅消息调度程序。您需要向调度程序注册事件,然后适当地调用它们。这使您可以很容易地连接不同的代码片段。我想不出不使用委托的方法

              【讨论】:

                【解决方案11】:

                假设您有一个响应不同按键的控制台应用程序:

                            Action a = () => {/* Do Stuff*/};
                            Action b = () => {/* Do Stuff*/};
                            Action c = () => {/* Do Stuff*/};
                            Action d = () => {/* Do Stuff*/};
                            Action e = () => {/* Do Stuff*/};
                            Action f = () => {/* Do Stuff*/};
                            Action g = () => {/* Do Stuff*/};
                            Action h = () => {/* Do Stuff*/};
                            Action i = () => {/* Do Stuff*/};
                            Action j = () => {/* Do Stuff*/};
                
                            List<Action> actions = new List<Action>() {a,b,c,d,e,f,g,h,i,j};
                
                            string line;
                
                            while((line = Console.ReadKey().KeyChar) != 'q')
                            {
                                if(line.isBetween_0_and_9())
                                {
                                    actions[line.ParseInt()]();
                                }
                            }
                

                您显然可以只使用一堆 If,但这不仅更容易,而且几乎可以肯定更清晰/可读。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-28
                  • 2015-01-07
                  • 2020-11-07
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多