Action / Func vs Methods,有什么意义?

我知道如何在.NET中使用ActionFunc ,但每次我开始时,使用我调用的常规旧方法可以实现完全相同的解决方案。

ActionFunc被用作我无法控制的事物的参数时,这就排除了,比如LINQ的.Where

基本上我的问题是……为什么这些存在? 他们给了我什么额外的和新的一个简单的方法不?

我认为这里的其他答案谈论Action / Func是什么及其用途。 我将尝试回答如何在Action / Func和方法之间进行选择。 差异首先:

1)从原始性能的角度来看, 与直接方法调用相比 , 代表速度较慢 ,但是如此令人担忧,以至于担心它是一种不好的做法。

2)方法可以有重载(具有不同签名的相同函数名)但不具有Action / Func委托,因为它们被声明为变量,并且通过C#规则,您不能在给定范围内具有两个具有相同名称的变量。

 bool IsIt() { return 1 > 2; } bool IsIt(int i) { return i > 2; } //legal Func IsIt = () => 1 > 2; Func IsIt = i => i > 2; //illegal, duplicate variable naming 

3)因此, Action / Func是可重新分配的,可以指向任何函数,而编译后的方法永远都是相同的。 如果它指向的方法在运行时期间永远不会改变,则使用Func/Action在语义上是错误的。

 bool IsIt() { return 1 > 2; } //always returns false Func IsIt = () => 1 > 2; IsIt = () => 2 > 1; //output of IsIt depends on the function it points to. 

4)您可以为常规方法指定ref / out参数。 例如,你可以拥有

 bool IsIt(out string p1, ref int p2) { return 1 > 2; } //legal Func IsIt; //illegal 

5)您不能为Action / Func引入新的generics类型参数(它们是通用的btw,但类型参数只能是父方法或类中指定的已知类型),与方法不同。

 bool IsIt() { return 1 > 2; } //legal Func IsIt = () => 1 > 2; //illegal 

6)方法可以有可选参数,而不是Action / Func

 bool IsIt(string p1 = "xyz") { return 1 > 2; } //legal Func IsIt = (p1 = "xyz") => 1 > 2; //illegal 

7)你可以使用params关键字作为方法的参数,而不是Action / Func

 bool IsIt(params string[] p1) { return 1 > 2; } //legal Func IsIt = p1 => 1 > 2; //illegal 

8) Intellisense可以很好地使用方法的参数名称(因此你有很酷的XML文档可用于方法),而Action / Func不然。 因此,就可读性而言,常规方法获胜。

9) Action / Func的参数限制为16(不是你不能用更多的定义你自己的参数限制),但方法支持的比你需要的多。

至于何时使用哪个,我会考虑以下几点:

  1. 当你被迫使用基于上述任何一点的那个,那么你无论如何都别无选择。 第3点是我发现最引人注目的,你必须根据自己的决定作出决定。

  2. 在大多数正常情况下,常规方法是可行的方法。 这是在C#和VB.NET世界中重构一组通用function的标准方法。

  3. 根据经验,如果函数不仅仅是一行,我更喜欢一种方法。

  4. 如果函数在特定方法之外没有相关性且函数太简单了,比如简单的选择器( Func )或谓词( Func ),我更喜欢Action / Func 。 例如,

     public static string GetTimeStamp() { Func f = dt => humanReadable ? dt.ToShortTimeString() : dt.ToLongTimeString(); return f(DateTime.Now); } 
  5. 可能存在Action / Func更有意义的情况。 例如,如果你必须构建一个繁重的表达式并编译一个委托,那么它只值得做一次并缓存已编译的委托。

     public static class Cache { public static readonly Func Get = GetImpl(); static Func GetImpl() { //some expensive operation here, and return a compiled delegate } } 

    代替

     public static class Cache { public static T Get() { //build expression, compile delegate and invoke the delegate } } 

    在第一种情况下,当您调用GetGetImpl只执行一次,而在第二种情况下,(昂贵) Get将每次调用。


不要忘记匿名方法本身将具有与Func/Action无关的某些限制 ,使得使用方法略有不同。 另请参阅此相关问题。

Action和Func是框架提供的委托类型。 委托允许将函数视为变量,这意味着您可以(除其他外)将它们从方法传递给方法。 如果您曾使用C ++编程,则可以将Delegates视为受其引用的方法的签名限制的函数指针。

Action和Func特别是通用委托(意思是它们采用类型参数)和一些最常见的签名 – 几乎所有程序中的任何方法都可以使用这两个中的一个或另一个来表示,从而节省了人们大量时间手动定义委托我们在版本2之前的.net中做过。实际上,当我在项目中看到这样的代码时,我通常可以安全地假设项目是从.net 1.1迁移的:

 // This defines a delegate (a type that represents a function) // but usages could easily be replaced with System.Action delegate void SomeApplicationSpecificName(String someArgument); 

我建议你再看一下代表们。 它们是C#语言的一个非常强大的function。

在很多情况下,Func可以帮助方法不会。

 public void DoThing(MyClass foo, Func func) { foo.DoSomething; var result = func(foo); foo.DoStringThing(result); } 

因此,无论何时调用此方法,您都可以指定不同的Func – DoThing方法不需要知道正在执行的操作,只要不管它是什么都会返回一个字符串。

您可以通过使用delegate关键字而不使用Func关键字来执行此操作; 它的工作方式大致相同。

我用它们来创建一个函数数组。 例如,我可能有一个ComboBox充满了可以采取的行动。 我用一个类或结构的项填充ComboBox:

 public class ComboBoxAction { private string text; private Action method; public ComboBoxAction(string text, Action method) { this.text = text; this.method = method; } public override string ToString() { return this.text; } public void Go() { this.method(); } } 

然后当有人选择一个项目时,我可以调用该动作。

 CType(ComboBox1.SelectedItem, ComboBoxAction).Go() 

这比使用Select语句根据ComboBox的文本确定调用哪个方法要容易得多。

actionfunc一个很好的用途是当我们需要执行某些操作时(在方法之前或之后),而不管方法是什么。 例如,如果发生exception,我们需要重试该方法10次。

请考虑以下方法 – 其返回类型是generic 。 所以它可以应用于任何返回类型的func

 public static T ExecuteMultipleAttempts(Func inputMethod, Action additionalTask, int wait, int numOfTimes) { var funcResult = default(T); int counter = 0; while (counter < numOfTimes) { try { counter++; funcResult = inputMethod(); //If no exception so far, the next line will break the loop. break; } catch (Exception ex) { if (counter >= numOfTimes) { //If already exceeded the number of attemps, throw exception throw; } else { Thread.Sleep(wait); } if (additionalTask != null) { additionalTask(); } } } return funcResult; }