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

时间:2010-12-19 10:58:57

标签: c# delegates 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>

8 个答案:

答案 0 :(得分:65)

ActionFunc系列代表的出现使自定义代理的使用减少,但后者仍然可以找到用途。自定义代表的优点包括:

  1. 正如其他人所指出的那样,与通用ActionFunc不同,传达意图的明确意图(Patrik对于有意义的参数名称有一个非常好的观点)。

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

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

    但不是

    Func<out string, ref int, double> ChangeListAction;
    
  3. 此外,对于自定义委托,您只需要在代码库中的某处编写ChangeListAction(我的意思是定义),而如果您没有定义一个,则必须在任何地方乱丢{{ 1}}到处都是。在后一种情况下改变签名将是一个麻烦 - 一个不干的坏情况。

  4. 可以有可选参数。

    Func<string, int, double>

    但不是

    public delegate double ChangeListAction(string p1 = "haha", int p2);
    
  5. 您可以使用Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2; 关键字作为方法的参数,而不是使用params

    Action/Func

    但不是

    public delegate double ChangeListAction(int p1, params string[] p2);
    
  6. 好吧,如果你真的运气不好,需要参数超过16(目前):)


  7. 至于Func<int, params string[], double> ChangeListAction; Action的优点:

    1. 这很快又脏,我全身都用了。如果用例很简单,它会使代码变短(自定义委托与我一起过时)。

    2. 更重要的是,它的类型跨域兼容。 FuncAction是框架定义的,只要参数类型匹配,它们就可以无缝运行。您Func不能ChangeSomeActionChangeListAction很好地利用了这一方面。

答案 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!!