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

Posted

技术标签:

【中文标题】手动创建委托与使用 Action/Func 委托【英文标题】:Creating delegates manually vs using Action/Func delegates 【发布时间】:2011-05-27 19:48:54 【问题描述】:

今天我正在考虑宣布这一点:

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&lt;T&gt;,而 .NET 3.5 出现了 Func&lt;T&gt;

【问题讨论】:

【参考方案1】:

ActionFunc 委托家族的出现使得自定义委托的使用减少了,但后者仍然找到了用途。自定义委托的优点包括:

    正如其他人所指出的,与通用 ActionFunc 不同,它清楚地传达了意图(Patrik 对有意义的参数名称有很好的看法)。

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

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

    但不是

    Func<out string, ref int, double> ChangeListAction;
    
    1234563超过。在后一种情况下更改签名会很麻烦 - 不干的坏情况。

    可以有可选参数。

    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 个的参数(目前):)


关于ActionFunc的优点:

    它又快又脏,我一直在用它。如果用例很简单(自定义委托对我来说已经过时了),它会缩短代码。

    更重要的是,它的类型跨域兼容。 ActionFunc 是框架定义的,只要参数类型匹配,它们就可以无缝运行。 ChangeSomeAction 不能用于 ChangeListActionLinq 很好地利用了这个方面。

【讨论】:

这是另一个你不能使用 Func 的地方 - 如果它必须返回自己,你就不能使用 Func,如此处提到的:***.com/questions/27989296/… 感谢@Marwie。有帮助。会在某个时候添加到我的答案中。 在例子#5中params必须是最后一个参数。 @Jalal 你是对的。愚蠢的错误。任何人都可以随时编辑答案:) 您在“LINQ”中提到了这一点。我相信 Action/Func 是匿名(闭包)方法的基础。【参考方案2】:

优点是清晰。通过为类型指定一个明确的名称,读者可以更清楚地了解它的作用。

它还会在您编写代码时为您提供帮助。像这样的错误:

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

没有那么有用:

cannot convert from CreateListAction to UpdateListAction

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

【讨论】:

Func 的名称类似于 Func 所以编译器不应该说:Can not convert from Func1Name to Func2Name then it would not be不太有用。【参考方案3】:

显式声明委托可以帮助进行一些类型检查。编译器可以确保分配给变量的委托旨在用作 ChangeListAction,而不是一些恰好与签名兼容的随机操作。

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

在设计像 LINQ 这样的通用库时,应该使用 Func、Predicate 和 Action 委托。在这种情况下,除了它们将执行和操作或用作谓词这一事实之外,委托没有预定义的语义。

在旁注中,元组与匿名类型与声明自己的类之间存在类似的权衡问题。您可以将所有内容都粘贴在 Tuple 中,但属性只是 Item1、Item2,它没有说明类型的使用。

【讨论】:

【参考方案4】:

正如一些答案提到的那样,胜利是清晰的,你命名类型,这样你的 api 用户会更容易理解。我想说 - 在大多数情况下 - 为您的公共 api 声明委托类型,但在内部使用 Func&lt;?,?&gt; 是完全可以的。

声明其他答案中未提及的委托类型的一个巨大好处是,除了为类型命名之外,您实际上还可以为参数命名,这将大大提高可用性。

【讨论】:

【参考方案5】:

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

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&lt;T&gt;/Func&lt;T&gt; ?

的合理答案

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

要获得更好和更详细的答案,请查看@nawfal。我会尽量简单一点。

你声明了一个类的成员,所以你应该坚持使用委托。使用delegate 更具描述性和结构性。

Action/Func 类型是为传递而设计的,因此您应该更多地将它们用作参数和局部变量。

实际上它们都继承了Delegate 类。 Action 和 Func 是泛型类型,可以简化创建具有不同参数类型的委托。而 delegate 关键字实际上在一个声明中创建了继承自 Delegate 的全新类。

【讨论】:

【参考方案8】:

正如MSDN 所说,Func&lt;&gt; 本身就是预定义的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 = a => "ttt";
//----------
// Long Version Anonymous Function
Func<string, string> fLong = delegate(string a)

  return "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&lt;&gt;委托的参数。我们可以用这个方法做的事情是

DeclareFunc&lt;&gt; 类型的委托并将其传递给函数而不是 Define 自定义委托。

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

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

【讨论】:

以上是关于手动创建委托与使用 Action/Func 委托的主要内容,如果未能解决你的问题,请参考以下文章

Func与Action

在一行中声明和定义一个委托(不使用 Action/Func)

委托,C#本身的委托(Action Func)

委托delegate,Action,Func,Predicate

《C#零基础入门之百识百例》(七十五)内置委托 -- Action/Func

Delegate,Action,Func,匿名方法,匿名委托,事件