手动创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,但在概念上做两个完全不同的事情,编译器可以确保你不会意外地使用另一个。
Action
和Func
系列代表的出现使得习惯代表使用不足,但是后者仍然被使用。 自定义代表的优点包括:
-
正如其他人所指出的,与通用的
Action
和Func
( Patrik对于有意义的参数名称有一个非常好的观点)可以清楚地expression意图。 -
您可以指定
ref
/out
参数,与其他两个通用代理不同。 例如,你可以拥有public delegate double ChangeListAction(out string p1, ref int p2);
但不是
Func<out string, ref int, double> ChangeListAction;
-
另外,对于自定义委托,您只需要在代码库中的某个地方写一个
ChangeListAction
(我的意思是定义),而如果不定义一个,则必须在每一处遍历Func<string, int, double>
。 在后一种情况下更改签名将是一个麻烦 – 一个不干的情况。 -
可以有可选的参数。
public delegate double ChangeListAction(string p1 = "haha", int p2);
但不是
Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2;
-
你可以有一个方法的参数
params
关键字,而Action/Func
不是这样的。public delegate double ChangeListAction(int p1, params string[] p2);
但不是
Func<int, params string[], double> ChangeListAction;
-
那么,如果你真的运气不好,需要超过16个参数(目前):)
至于Action
和Func
优点:
-
这是快速和肮脏的,我用完了。 如果用例很琐碎(自定义代理已经不适合我),它使得代码变得简短。
-
更重要的是,它的types跨域兼容。
Action
和Func
是框架定义的,只要参数types匹配,它们就可以无缝运行。ChangeListAction
不能有ChangeSomeAction
。Linq
发现这个方面很好用。
明确声明委托可以帮助进行一些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/Func
types用于传递,因此您应该更多地使用它们作为参数和局部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!!