74

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

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.0Action<T>和 .NET 3.5 的出现Func<T>

4

8 回答 8

85

Action委托及其家族的出现Func使得自定义委托的使用较少,但后者仍然可以找到用途。自定义委托的优点包括:

  1. 正如其他人所指出的那样,与泛型不同,它清楚地传达了意图ActionFuncPatrik有意义的参数名称有一个很好的观点)。

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


Action至于和的优点Func

  1. 它又快又脏,我一直在用它。如果用例是微不足道的(自定义委托对我来说已经过时了),它会使代码变短。

  2. 更重要的是,它的类型跨域兼容。Action并且Func是框架定义的,只要参数类型匹配,它们就可以无缝运行。你不能拥有ChangeSomeActionfor ChangeListActionLinq发现这方面的用途很大。

于 2013-05-15T10:41:39.363 回答
62

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

在您编写代码时,它也会对您有所帮助。像这样的错误:

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

不如说的有用:

cannot convert from CreateListAction to UpdateListAction

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

于 2010-12-19T11:02:48.287 回答
10

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

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

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

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

于 2010-12-19T11:06:13.160 回答
8

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

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

于 2010-12-19T11:10:48.663 回答
7

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

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> ?

于 2014-12-05T03:56:00.613 回答
4

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

于 2010-12-19T11:07:47.140 回答
2

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

您正在声明一个类的成员,因此您应该坚持使用委托。使用delegate更具描述性和结构性。

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

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

于 2016-10-20T08:04:19.070 回答
1

正如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 = 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<>委托的参数。我们可以用这个方法做的事情是

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

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

string[] strList = { "abc", "abcd", "abcdef" };
strList.Select(fAssign); // is valid
//strList.Select(customDlg); // Compilation Error!!
于 2016-08-18T10:30:40.770 回答