手动创build委托与使用Action / Func委托

今天我正在考虑宣布:

private delegate double ChangeListAction(string param1, int number); 

但为什么不使用这个:

 private Func<string, int, double> ChangeListAction; 

或者如果ChangeListAction没有返回值我可以使用:

 private Action<string,int> ChangeListAction; 

那么在使用delegate关键字声明委托的优势在哪里呢?

是因为.NET 1.1,而.NET 2.0来了Action<T>和.NET 3.5来了Func<T>

优点是清晰。 通过给这个types一个明确的名字,读者会更清楚它的function。

编写代码时,它也可以帮助你。 像这样的错误:

 cannot convert from Func<string, int, double> to Func<string, int, int, double> 

比那个说:

 cannot convert from CreateListAction to UpdateListAction 

这也意味着,如果你有两个不同的代表,它们都采用相同的参数types,但在概念上做两个完全不同的事情,编译器可以确保你不会意外地使用另一个。

ActionFunc系列代表的出现使得习惯代表使用不足,但是后者仍然被使用。 自定义代表的优点包括:

  1. 正如其他人所指出的,与通用的ActionFunc ( Patrik对于有意义的参数名称有一个非常好的观点)可以清楚地expression意图。

  2. 您可以指定ref / out参数,与其他两个通用代理不同。 例如,你可以拥有

     public delegate double ChangeListAction(out string p1, ref int p2); 

    但不是

     Func<out string, ref int, double> ChangeListAction; 
  3. 另外,对于自定义委托,您只需要在代码库中的某个地方写一个ChangeListAction (我的意思是定义),而如果不定义一个,则必须在每一处遍历Func<string, int, double> 。 在后一种情况下更改签名将是一个麻烦 – 一个不干的情况。

  4. 可以有可选的参数。

     public delegate double ChangeListAction(string p1 = "haha", int p2); 

    但不是

     Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2; 
  5. 你可以有一个方法的参数params关键字,而Action/Func不是这样的。

     public delegate double ChangeListAction(int p1, params string[] p2); 

    但不是

     Func<int, params string[], double> ChangeListAction; 
  6. 那么,如果你真的运气不好,需要超过16个参数(目前):)


至于ActionFunc优点:

  1. 这是快速和肮脏的,我用完了。 如果用例很琐碎(自定义代理已经不适合我),它使得代码变得简短。

  2. 更重要的是,它的types跨域兼容。 ActionFunc是框架定义的,只要参数types匹配,它们就可以无缝运行。 ChangeListAction不能有ChangeSomeActionLinq发现这个方面很好用。

明确声明委托可以帮助进行一些types检查。 编译器可以确保分配给该variables的委托用作ChangeListAction,而不是一些随机的动作,它恰好与签名兼容。

然而,声明自己的委托的真正价值在于它赋予了它的语义含义。 阅读代码的人将会知道委托人的名字。 想象一下,如果你有一个有三个int字段的类,而是你声明了一个由三个int元素组成的数组。 数组可以做同样的事情,但是这些字段的名称会带来对开发人员有用的语义信息。

当你devise一个像LINQ的通用库时,你应该使用Func,Predicate和Action委托。 在这种情况下,委托没有预定义的语义,除了它们将执行和操作或用作谓词。

在一个侧面说明有一个类似的折衷与Tuple与匿名types的问题与宣布你自己的类。 你可以把所有东西都粘在一个元组中,但是这些属性只是Item1,Item2,它没有提供任何关于types的使用。

正如一些答案所提到的,胜利是清晰的,你可以命名这个types,所以你的api的用户会更容易理解。 我会说 – 在大多数情况下 – 为你的公共apis声明委托types,但是在内部使用Func<?,?>是相当好的。

声明在其他答案中没有提到的委托types的一个巨大好处是,除了给这个types一个实际得到的名字外,这将大大提高可用性。

我发现了一个特殊的用例,你只能使用委托:

 public delegate bool WndEnumProc(IntPtr hwnd, IntPtr lParam); [DllImport("User32.dll")] public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam); 

使用Func / Action不起作用: 'Namespace.Class.WndEnumProc' is a 'field' but is used like a 'type'

 public Func<IntPtr, IntPtr, bool> WndEnumProc; [DllImport("User32.dll")] public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam); 

下面的代码不会编译,但运行时会抛出exception,因为System.Runtime.InteropServices.DllImportAttribute不支持genericstypes的编组:

 [DllImport("User32.dll")] public static extern bool EnumWindows(Func<IntPtr, IntPtr, bool> lpEnumFunc, IntPtr lParam); 

我提出这个例子来向每一个人展示:有时代表是你唯一的select。 这是一个合理的答案, why not use Action<T>/Func<T> ?

当你开始在Func / Action中获得太多的参数时,声明委托,否则你不得不回头问:“第二个int是什么意思?

为了更好,更详细的答案看@nawfal。 我会尽量更简单。

你正在宣布一个类的成员,所以你应该坚持与委托。 使用delegate更具描述性和结构性。

Action/Functypes用于传递,因此您应该更多地使用它们作为参数和局部variables。

实际上这两个都是inheritanceDelegate类。 Action和Func是genericstypes,可以简化创build具有不同参数types的委托。 而delegate关键字实际上是在一个声明中创build了从Delegateinheritance的全新类。

正如MSDN所说, Func<>本身是预定义的Delegate 。 这是我第一次对这个东西感到困惑。 经过实验,我的理解比较清楚。 通常,在C#中,我们可以看到

Type为指向Instance的指针。

相同的概念适用于

Delegate为指向Method的指针

这些东西之间的区别是Delegate不具备面向对象的概念,例如Inheritance 。 为了使这个事情更清楚,我做了实验

 public delegate string CustomDelegate(string a); // Func<> is a delegate itself, BUILD-IN delegate //========== // Short Version Anonymous Function //---------- Func<string, string> fShort = delegate(string a) { return "ttt"; }; // Long Version Anonymous Function //---------- Func<string, string> fLong = a => "ttt"; MyDelegate customDlg; Func<string, string> fAssign; // if we do the thing like this we get the compilation error!! // because fAssign is not the same KIND as customDlg //fAssign = customDlg; 

框架中的许多内置方法(例如,LINQ),接收Func<>委托的参数。 我们可以用这种方法做的事情是

Declare Func<>types的委托并将其传递给函数,而不是Define自定义委托。

例如,从上面的代码我添加更多的代码

 string[] strList = { "abc", "abcd", "abcdef" }; strList.Select(fAssign); // is valid //strList.Select(customDlg); // Compilation Error!!