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


71

今天,我正在考虑声明以下内容:

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>

Answers:


56

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

当您编写代码时,它也将为您提供帮助。像这样的错误:

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

没有说以下内容那么有用:

cannot convert from CreateListAction to UpdateListAction

这也意味着,如果您有两个不同的委托,这两个委托都使用相同类型的参数,但从概念上讲,它们执行两项完全不同的操作,则编译器可以确保您不会意外地在原意中使用另一个。


1
Func <T1,T2,U>具有类似于Func <T1,T2,T3,U>的名称,因此编译器不应该说:无法从Func1Name转换为Func2Name,那么它的帮助就不会减少。
伊丽莎白2010年

79

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

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


至于案情ActionFunc

  1. 它又快又脏,我用完了。如果用例很琐碎(自定义委托与我不合时宜),它会使代码简短。

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


2
这里是另外一个,你不能使用Func键-你不能使用Func键,如果它有返回自己这里mentionned:stackoverflow.com/questions/27989296/...
Marwie

谢谢@Marwie。很有帮助 有时会增加我的答案。
nawfal 2015年

在示例中#5params必须是最后一个参数。
贾拉勒

@Jalal你是对的。愚蠢的错误。任何人都可以随时编辑答案:)
nawfal '16

您用“ LINQ”暗示了这一点。我相信Action / Func是匿名(关闭)方法的基础。
Todd

10

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

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

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

附带说明一下,Tuple与匿名类型与声明自己的类之间存在类似的权衡问题。您可以将所有内容都粘贴到一个元组中,但是属性只是Item1和Item2,而这并不能说明类型的使用。


7

正如一些答案提到的那样,胜利是明确的,您可以为类型命名,因此对于您的api用户而言将更容易理解。我想说-在大多数情况下-为您的公共api声明委托类型,但是使用起来还是可以的Func<?,?>内部。

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


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


4

当您开始在Func / Action中获取太多参数时,明确声明该委托,否则您将不得不回头看看,“第二个int再次意味着什么?”


2

有关更好和更详尽的答案,请查看@nawfal。我会尽量简化。

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

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

实际上这两个都是继承Delegate类。Action和Func是通用类型,可简化创建具有不同参数类型的委托。实际上,委托关键字在一个声明中创建了一个完整的新类,该类继承自Delegate。


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<>委托的参数。我们可以用这种方法做的是

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

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

string[] strList = { "abc", "abcd", "abcdef" };
strList.Select(fAssign); // is valid
//strList.Select(customDlg); // Compilation Error!!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.