新手代理基础知识 C#

4

我觉得我不理解代理方法的作用。所有我看到的例子都像这样做:

class DelegateClass
{
    private List<string> ListString = new List<string>;
    delegate void DoSomethingToStringsDelegate(string s);

    public base()
    {
         ListString.Add("string"); ....
    }

    void ProcessStrings(DoSomethingToStringsDelegate dstsd)
    {
        foreach(string s in ListString)
            dstsd(s);
    }
}

class AClass
{
    ...
    void UseDelegateMethod(...)
    {
        DelegateClass ds = new DelegateClass();
        ds.ProcessStrings(new DoSomethingToStringsDelegate(PrintStrings);
    }
    void PrintStrings(string s)
    {
        System.out.Write(s);
    }
}

我不理解为什么需要这个,因为你可以实现一个getListStrings()并遍历字符串,像代理一样做你需要做的事情。

foreach( string s in ds.ggetListStrings() )
    System.out.Write(s);

私有成员的原因并不合理,因为我可以这样做:

global List<Strings> myListStrings = new List<Strings>();
ds.ProcessStrings(new DoSomethingToStringsDelegate(GetStrings);

void GetStrings(string s)
{
    myListStrings.Add(s);
}

......现在我有了与getListStrings()相同的列表....

有人能解释一下吗?非常感谢!


那个例子看起来有点复杂。我可能不会像这样编写它,尽管我需要知道更大的上下文;-) 然而,委托(delegate/Action/Func)的“重点”在于它可以在不同的上下文中被调用:例如作为回调。相反,考虑使用Regex.Replace(string,string,MatchEvaluator),它展示了一个非常有用的委托(MatchEvaluator)的使用方式。(你曾经试过在Java中进行类似的正则表达式替换吗?这并不好玩。) - user166390
可能是 https://dev59.com/5nI-5IYBdhLWcg3wCzpn 的重复问题。 - cadrell0
虽然如此,你难道不能只是这样做吗:如果(MatchEvaluator) Regex.Replace(string, string)? - Tizz
5个回答

4
Delegate非常有用,因为它实际上充当了任何以string作为参数并返回void的方法的占位符。
如果您熟悉C语言,它类似于函数指针的工作方式。在其位置上,可以传递与签名和返回类型匹配的任何方法。
例如,假设我想实现一个对对象组进行排序的方法。除了对象列表之外,我还可以传递指示如何进行排序的委托。由于可以传递与委托匹配的任何方法,因此如果我想要递减或递增排序,我可以动态地在不同的方法之间切换:
 delegate int comparisonDelegate(int p1, int p2);

 void SortArray(int[] array, comparisonDelegate cmp)
 {
      // determine order according to cmp
 }

 int CompareDecreasing(int p1, int p2)
 {
     if(p1 > p2) return -1;
     if(p1 < p2) return 1;
     return 0;
 }

 int CompareIncreasing(int p1, int p2)
 {
     if(p1 > p2) return 1;
     if(p1 < p2) return -1;
     return 0;
 }

现在我可以这样调用SortArray:

 SortArray(array, new comparisonDelegate(compareDecreasing));
 SortArray(array, new comparisonDelegate(compareIncreasing));

你的示例没有定义任何“compareIncreasing”方法,相反你有两个相同的“compareDecreasing”签名... ;) - Nuffin

3
我不明白为什么需要这个,当你可以直接实现一个 getListStrings() 并自己迭代字符串并执行所需操作,就像委托一样。
目标是创建一个可在集合上工作但可以执行任何操作的函数。
通过示例更容易理解-要查看如何以及为什么有用,请查看 LINQ to Objects 的优秀示例。
假设您想查看有多少字符串大于4个字符-Enumerable.Count 方法具有接受委托的重载-可以使用 Func<T,bool> 谓词。这使您可以指定任何操作并计算元素数:
List<string> listOfStrings = GetListOfStrings();

int countOfStringsGreaterThanFourChars = listOfStrings.Count(s => s.Length > 4);

在这里,我们传递了一个通过lambda表达式创建的委托,它给出了我们的条件。通过使用接受委托的Count方法,它适用于任何条件,所以我们不必每次想要不同条件时重新实现它。
比如说,如果我们想知道以"E"开头的字符串有多少个,我们可以简单地使用:
int countOfStringsStartingWithE = listOfStrings.Count(s => s.StartsWith("E"));

同样的,我们只需要编写与我们特定需求相关的代码,而不是重复所有必须通过循环遍历集合并计算项目数所需的样板代码...


1

0

从事件的角度来考虑。假设您有一个类,对一系列项目进行处理,并且对于每个项目,使用您的类的某个人可能希望被通知已处理该项目(可能更新进度条或更新系统的其他部分等)。让我们暂时搁置委托,看看如何使用接口实现此功能:

public class MyClassThatDoesSomething
{
   private List<string> list = new List<string>();
   public void ProcessList()
   {
      foreach(var item in list)
      {
         ProcessItem(item);
         //how do we notify someone here??
      }
   }

   private void ProcessItem(string item){}
}

现在假设有人正在使用这个类:

var mc = new MyClassThatDoesSomething();
mc.ProcessList(); //how do I know when each one has been processed?

为了解决这个问题,让我们创建一个接口:
public interface IItemProcessed
{
   void ItemProcessed(string item);
}

我们现在可以重构我们的原始类:
public class MyClassThatDoesSomething
{
   private List<string> list = new List<string>();
   public void ProcessList()
   {
      foreach(var item in list)
      {
         ProcessItem(item);
         //how do we notify someone here??
         if(this.Listener != null)
         {
             this.Listener.ItemProcessed(item);
         }
      }
   }

   private void ProcessItem(string item){}

   public IItemProcessed Listener {get;set;}
}

而您的类的使用者现在可以这样做:

public class ProcessListener : IItemProcessed
{
   public void ItemProcessed(string item)
   {
      Console.WriteLine(item);
      //update progress bar, whatever
   }
}

var mc = new MyClassThatDoesSomething();
mc.Listener = new ProcessListener();
mc.ProcessList(); 

既然你已经理解了这一点,你可以将委托视为小型接口,然后将原始类更改为以下内容:

public class MyClassThatDoesSomething
{
   private List<string> list = new List<string>();
   public void ProcessList()
   {
      foreach(var item in list)
      {
         ProcessItem(item);
         //how do we notify someone here??
         if(this.Callback != null)
         {
             this.Callback(item);
         }
      }
   }

   private void ProcessItem(string item){}

   public Action<string> Callback {get;set;}
}

以及消费者:

var mc = new MyClassThatDoesSomething();
mc.Listener = s =>
{
   Console.WriteLine(s);
   //update progress bar, whatever
}
mc.ProcessList(); 

总之,你可以把委托看作是一种方式,让外部用户能够在你的代码里提供小段逻辑(比如 Linq 中的集合过滤),或者为回调/事件(就像我上面演示的)提供一个简单的“钩子”。

0
一方面,它允许您使用相同的方法签名注入不同的行为。在某些情况下,您可能只想简单地添加到列表中。在另一种情况下,您可能需要将其添加到列表并写入日志文件,或者执行其他您想要在DoSomethingToStringsDelegate中执行的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接