动作/功能与方法,有什么意义?

发布于 2024-12-07 20:34:57 字数 260 浏览 2 评论 0原文

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

这排除了将 ActionFunc 用作我无法控制的内容的参数的情况,例如 LINQ 的 .Where

所以基本上我的问题是......为什么这些存在?它们给了我什么是简单方法所没有的额外的和新的东西?

I know how to use Action and Func in .NET, but every single time I start to, the exact same solution can be achieved with a regular old Method that I call instead.

This excludes when an Action or Func is used as an argument for something I don't control, like LINQ's .Where.

So basically my question is...why do these exist? What do they give me extra and new that a simple Method doesn't?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

剩一世无双 2024-12-14 20:34:57

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

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

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

bool IsIt() { return 1 > 2; }
bool IsIt(int i) { return i > 2; } //legal

Func<bool> IsIt = () => 1 > 2; 
Func<int, bool> IsIt = i => i > 2; //illegal, duplicate variable naming

3) 因此,Action/Func 是可重新分配的,可以指向任何函数,而方法一旦编译就永远保持不变。如果Func/Action指向的方法在运行时从未改变,那么它在语义上是错误的。

bool IsIt() { return 1 > 2; } //always returns false

Func<bool> 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<out string, ref int, bool> IsIt; //illegal

5) 您不能为 Action/Func 引入新的泛型类型参数(顺便说一句,它们已经是泛型的,但是类型与方法不同,参数只能是已知类型或在父方法或类中指定的类型)。

bool IsIt<A, R>() { return 1 > 2; } //legal

Func<bool> IsIt<A, R> = () => 1 > 2; //illegal

6) 方法可以有可选参数,但 Action/Func 除外。

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

Func<string, bool> IsIt = (p1 = "xyz") => 1 > 2; //illegal

7) 您可以使用 params 关键字作为方法的参数,但 Action/Func 则不然。

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

Func<params string[], bool> IsIt = p1 => 1 > 2; //illegal

8) Intellisense 可以很好地处理方法的参数名称(因此您可以为方法提供很酷的 XML 文档),但 Action/Func 则不然。因此,就可读性而言,常规方法胜出。

9) Action/Func 的参数限制为 16 个(并不是说你不能定义更多的参数),但是 方法支持的比你多永远会

至于什么时候用哪个,我会考虑以下几点:

  1. 当你基于上述任何一点而被迫使用一个时,那么你无论如何都没有其他选择。 我发现第 3 点是最引人注目的,您必须以此为基础做出决定。

  2. 在大多数正常情况下,常规方法是正确的选择。这是在 C# 和 VB.NET 世界中重构一组常见功能的标准方法。

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

  4. 如果函数与特定方法之外没有相关性,并且函数过于琐碎,例如简单的选择器 (Func) 或谓词 (Func))我更喜欢Action/Func。例如,

    公共静态字符串 GetTimeStamp() 
    {
        Func<日期时间,字符串> f = dt =>人类可读 
                                       ? dt.ToShortTimeString() 
                                       : dt.ToLongTimeString();
        返回 f(DateTime.Now);
    }
    
  5. 在某些情况下,Action/Func 可能更有意义。例如,如果您必须构建一个繁重的表达式并编译一个委托,那么只值得执行一次并缓存已编译的委托。

    公共静态类Cache; 
    { 
        公共静态只读函数;获取 = GetImpl();
    
        静态函数获取Impl()
        {
            //这里进行一些昂贵的操作,并返回一个已编译的委托
        }
    }
    

    而不是

    公共静态类Cache; 
    {
        公共静态 T Get()
        {
            //构建表达式,编译委托并调用委托
        }
    }
    

    在第一种情况下,当您调用 Get 时,GetImpl 仅执行一次,而在第二种情况下,(昂贵)Get每次都会被调用。


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

I think other answers here talk about what an Action/Func is and its use. I will try to answer how to choose between Action/Func and method. The differences first:

1) From a raw performance point of view, delegates are slower compared to direct method calls, but it's so insignificant that worrying about it is a bad practice.

2) Methods can have overloads (same function names with different signatures) but not Action/Func delegates since they are declared as variables and by C# rules you cant have two variables with the same name in a given scope.

bool IsIt() { return 1 > 2; }
bool IsIt(int i) { return i > 2; } //legal

Func<bool> IsIt = () => 1 > 2; 
Func<int, bool> IsIt = i => i > 2; //illegal, duplicate variable naming

3) Consequently, Action/Func are reassignable and can point to any function, while methods once compiled remain to be the same forever. It is semantically wrong to use Func/Action if the method it points to never changes during run time.

bool IsIt() { return 1 > 2; } //always returns false

Func<bool> IsIt = () => 1 > 2; 
IsIt = () => 2 > 1; //output of IsIt depends on the function it points to.

4) You can specify ref/out parameters for normal methods. For eg, you can have

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

Func<out string, ref int, bool> IsIt; //illegal

5) You cannot introduce new generic type parameter for Action/Func (they are generic already btw, but the type arguments can only be a known type or types specified in the parent method or class), unlike methods.

bool IsIt<A, R>() { return 1 > 2; } //legal

Func<bool> IsIt<A, R> = () => 1 > 2; //illegal

6) Methods can have optional parameters, not Action/Func.

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

Func<string, bool> IsIt = (p1 = "xyz") => 1 > 2; //illegal

7) You can have params keyword for parameters of a method, not so with Action/Func.

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

Func<params string[], bool> IsIt = p1 => 1 > 2; //illegal

8) Intellisense plays well with parameter names of methods (and accordingly you have cool XML documentation available for methods), not so with Action/Func. So as far as readability is concerned, regular methods win.

9) Action/Func have a parameter limit of 16 (not that you can't define your own ones with more) but methods support more than you will ever need.

As to when to use which, I would consider the following:

  1. When you are forced to use one based on any of the above points, then you anyway have no other choice. Point 3 is the most compelling I find upon which you will have to base your decision.

  2. In most normal cases, a regular method is the way to go. It's the standard way of refactoring a set of common functionality in C# and VB.NET world.

  3. As a rule of thumb, if the function is more than a line, I prefer a method.

  4. If the function has no relevance outside a specific method and the function is too trivial, like a simple selector (Func<S, T>) or a predicate (Func<bool>) I would prefer Action/Func. For eg,

    public static string GetTimeStamp() 
    {
        Func<DateTime, string> f = dt => humanReadable 
                                       ? dt.ToShortTimeString() 
                                       : dt.ToLongTimeString();
        return f(DateTime.Now);
    }
    
  5. There could be situations where Action/Func makes more sense. For instance if you have to build a heavy expression and compile a delegate, its worth doing it only once and caching the compiled delegate.

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

    instead of

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

    In the first case when you call Get, GetImpl is executed only once, where as in the second case, (expensive) Get will be called every time.


Not to forget anonymous method itself will have certain limits unrelated to Func/Action, making the use little different. Also see this for a related question.

温柔一刀 2024-12-14 20:34:57

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

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<String>
delegate void SomeApplicationSpecificName(String someArgument);

我建议您更多地研究委托。它们是 C# 语言的一个非常强大的功能。

Action and Func are framework-provided Delegate types. Delegates allow functions to be treated like variables, meaning that you can (among other things) pass them from method to method. If you have ever programmed in C++, you can think of Delegates as function pointers that are restricted by the signature of the method they refer to.

Action and Func specifically are generic delegates (meaning they take type parameters) with some of the most common signatures- almost any method in most programs can be represented using one or the other of those two, saving people a lot of time manually defining delegates like we did in .net prior to version 2. In fact, when I see code like this in a project, I can usually safely assume that the project was migrated from .net 1.1:

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

I'd recommend that you look into delegates some more. They are a hugely powerful feature of the C# language.

梦幻的味道 2024-12-14 20:34:57

我用它们来创建函数数组。例如,我可能有一个包含可以采取的操作的组合框。我用类或结构的项目填充组合框:

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 的文本确定要调用哪个方法要容易得多。

I use them to create an array of functions. For instance, I may have a ComboBox full of actions that could be taken. I populate the ComboBox with items of a class or structure:

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();
    }
}

Then when someone selects an item, I can call the action.

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

This is far easier than having a Select statement determine which method to call based on the ComboBox's text.

氛圍 2024-12-14 20:34:57

在很多情况下,Func 可以提供帮助,而 Method 则无法提供帮助。

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

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

您可以在不使用 Func 关键字的情况下通过使用 delegate 关键字来完成此操作;它的工作原理大致相同。

There's plenty of cases where a Func can help where a Method wouldn't.

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

So you can specify a different Func whenever you call this method - the DoThing method doesn't need to know what's being done, just that whatever it is will return a string.

You can do this without using the Func keyword by using the delegate keyword instead; it works much the same way.

倥絔 2024-12-14 20:34:57

actionfunc 的一大用途是当我们需要执行某些操作(在方法之前或之后),无论该方法是什么。例如,如果出现异常,我们需要重试该方法10次。

考虑以下方法 - 它的返回类型是通用的。因此它可以应用于任何返回类型的 func 上。

public static T ExecuteMultipleAttempts<T>(Func<T> 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;
        }

One great use of action and func are when we need to perform some operation (before or after a method), irrespective of what the method is. For example, we need to retry the method 10 times if exception occurs.

Consider the following method – its return type is generic. So it can be applied on func with any return type.

public static T ExecuteMultipleAttempts<T>(Func<T> 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;
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文