今天我在考虑宣布这个:
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>
?
答案 0 :(得分:65)
Action
和Func
系列代表的出现使自定义代理的使用减少,但后者仍然可以找到用途。自定义代表的优点包括:
正如其他人所指出的那样,与通用Action
和Func
不同,传达意图的明确意图(Patrik对于有意义的参数名称有一个非常好的观点)。
与其他两个通用委托不同,您可以指定ref
/ out
个参数。例如,你可以拥有
public delegate double ChangeListAction(out string p1, ref int p2);
但不是
Func<out string, ref int, double> ChangeListAction;
此外,对于自定义委托,您只需要在代码库中的某处编写ChangeListAction
(我的意思是定义),而如果您没有定义一个,则必须在任何地方乱丢{{ 1}}到处都是。在后一种情况下改变签名将是一个麻烦 - 一个不干的坏情况。
可以有可选参数。
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);
好吧,如果你真的运气不好,需要参数超过16(目前):)
至于Func<int, params string[], double> ChangeListAction;
和Action
的优点:
这很快又脏,我全身都用了。如果用例很简单,它会使代码变短(自定义委托与我一起过时)。
更重要的是,它的类型跨域兼容。 Func
和Action
是框架定义的,只要参数类型匹配,它们就可以无缝运行。您Func
不能ChangeSomeAction
。 ChangeListAction
很好地利用了这一方面。
答案 1 :(得分:50)
优点是清晰度。通过给类型一个明确的名称,读者可以更清楚它的作用。
在编写代码时,它也会对您有所帮助。像这样的错误:
cannot convert from Func<string, int, double> to Func<string, int, int, double>
没有比说出来的那样有用:
cannot convert from CreateListAction to UpdateListAction
这也意味着如果你有两个不同的委托,它们都采用相同类型的参数,但在概念上做两件完全不同的事情,编译器可以确保你不会意外地使用另一个代表你的意思。 / p>
答案 2 :(得分:11)
明确声明委托可以帮助进行某些类型检查。编译器可以确保分配给变量的委托旨在用作ChangeListAction,而不是一些恰好与签名兼容的随机操作。
然而,声明自己的委托的真正价值在于它赋予它语义含义。阅读代码的人将通过其名称知道代表正在做什么。想象一下,如果你有一个包含三个int字段的类,而是声明了一个包含三个int元素的数组。数组可以做同样的事情,但字段的名称带来了对开发人员有用的语义信息。
在设计像LINQ这样的通用库时,应该使用Func,Predicate和Action委托。在这种情况下,委托没有预定义的语义,除了它们将执行和操作或用作谓词的事实。
另一方面,Tuple与匿名类型vs宣布自己的类有类似的权衡问题。你可以把所有东西都放在一个元组中,但是属性只是Item1,Item2,它没有说明该类型的使用。
答案 3 :(得分:7)
正如一些答案提到胜利是清晰的,你可以为类型命名,因此对于api的用户来说更容易理解。我会说 - 在大多数情况下 - 为你的公共api声明委托类型,但在内部使用Func<?,?>
是很好的。
声明其他答案中未提及的委托类型的一个巨大好处是,除了给类型命名之外,您实际上也可以命名参数,这将大大提高可用性。
答案 4 :(得分:6)
我找到了一个特殊用例,你只能使用委托:
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);
以下代码执行编译,但在运行时抛出异常,因为System.Runtime.InteropServices.DllImportAttribute
不支持封送泛型类型:
[DllImport("User32.dll")]
public static extern bool EnumWindows(Func<IntPtr, IntPtr, bool> lpEnumFunc, IntPtr lParam);
我提出这个例子向每个人展示:有时委托是你唯一的选择。这是对您的问题why not use Action<T>/Func<T> ?
答案 5 :(得分:5)
当你开始在Func / Action中获得太多参数时,显式地声明委托,否则你不得不回头看看,“第二个int又意味着什么?”
答案 6 :(得分:2)
为了更好更详细的回答,请查看@nawfal。我会尝试更简单化。
您正在声明一个班级的成员,因此您应该坚持使用委托。使用delegate
更具描述性和结构性。
Action/Func
类型用于传递,因此您应该将它们更多地用作参数和局部变量。
实际上这两个都继承了Delegate
类。 Action和Func是泛型类型,简化了使用不同参数类型创建委托的过程。而委托关键字实际上是在一个声明中创建了从Delegate继承的全新类。
答案 7 :(得分:0)
正如MSDN所说,Func<>
本身是预先定义的Delegate
。我第一次对这些东西感到困惑。经过实验,我的理解更加清晰。通常,在C#中,我们可以看到
Type
作为指向Instance
的指针。
同样的概念适用于
的指针
Delegate
作为指向Method
这些与事物之间的区别是Delegate
不具备OOP的概念,例如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<>
类型的委托并将其传递给函数,而不是Define
自定义委托。
例如,从上面的代码我添加更多代码
string[] strList = { "abc", "abcd", "abcdef" };
strList.Select(fAssign); // is valid
//strList.Select(customDlg); // Compilation Error!!