【问题标题】:Using optional and named parameters with Action and Func delegates对 Action 和 Func 委托使用可选参数和命名参数
【发布时间】:2013-11-03 10:11:50
【问题描述】:

为什么不能执行以下操作:

Func<int, int, int> sum = delegate(int x, int y = 20) { return x + y; };

Action<string, DateTime> print = 
    delegate(string message, DateTime datetime = DateTime.Now) 
    { 
        Console.WriteLine(message);
    };

sum(x: 20, y: 40);
print(datetime: DateTime.Now, message: "Hello");

只有命名参数的情况:

Func<int, int, int> sum = delegate(int x, int y) { return x + y; };

Action<string, DateTime> print = 
    delegate(string message, DateTime datetime) 
    { 
        Console.WriteLine("{0} {1}", message, datetime);
    };

Console.WriteLine(sum(y: 20, x: 40));
print(datetime: DateTime.Now, message: "Hello");

只有可选参数的情况:

Func<int, int, int> sum = delegate(int x, int y = 20) { return x + y; };

Action<string , DateTime> print = 
    delegate(string message, DateTime datetime = DateTime.Now)
    { 
        Console.WriteLine("{0} {1}",message, datetime);
    };

Console.WriteLine(sum(40));
print("Hello");

【问题讨论】:

  • 请说明您想要达到的目标。
  • @Sudhakar,OP 想要使用可选参数(即具有默认值的参数)定义 ActionFunc
  • @SriramSakthivel 这个 q 只有一部分是链接问题的副本。我认为我们最好保持开放状态。

标签: c# delegates


【解决方案1】:

C# 7 现在允许'local functions'。因此,您可以编写一个“普通”方法,而不是创建一个 Action&lt;T&gt;Func&lt;T&gt;。这意味着适用于默认参数的常规规则。

因此,您可以将某些逻辑范围限定为函数内部,而无需与委托语法发生冲突。

它也像闭包一样工作,因此您可以从“父”方法访问局部变量。

我在下面的 Microsoft 示例中添加了一个毫无意义的 throwAnException 可选参数。

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation(bool throwAnException = false)
    {
        if (throwAnException) { throw Exception("You asked me to do this"); }

        for (var c = start; c < end; c++)
            yield return c;
    }
}

【讨论】:

    【解决方案2】:

    如上所述here -

    可选参数是方法或委托的属性 范围。当您调用具有 在编译时已知可选参数,编译器将插入 调用点的可选参数值。

    运行时不知道可选参数,所以你不能做一个 调用时插入可选参数的委托。

    因此,要使用它,您必须提取在编译时已知的具体实现(自定义委托),并将调用站点的参数替换为可选参数,并且也可以使用命名参数。

    声明自定义委托 -

    public delegate int FuncDelegate(int x, int y = 20);
    

    现在你可以在方法体中使用它了-

    FuncDelegate sum = delegate(int x, int y) { return x + y; };
    int result = sum (x : 20, y: 40 );
    result = sum(20);
    

    另外,只有compile time constant can be used in default parameters list。 但是DateTime.Now is not a compile time constant 也不能用于为参数指定可选值。

    所以对于动作部分,这将起作用 -

    public delegate void ActionDelegate(string message,
                                        DateTime dateTime = default(DateTime));
    

    现在在这里使用委托 -

    ActionDelegate print =
                    delegate(string message, DateTime dateTime)
                    { Console.WriteLine(dateTime.ToString()); };
    print(dateTime: DateTime.Now, message: "SomeThing");
    

    【讨论】:

    • 我说的是 Action 和 Func 代表。这不起作用。
    • 是的,我提到的可选参数只能在编译时用于方法和委托。 Func 和 Actions 更多的是运行时而不是编译时。
    • 有点讽刺,因为Action&lt;T&gt;的定义是Encapsulates a method that has four parameters and does not return a value.
    【解决方案3】:

    可选参数部分你有答案。关于命名参数,它完全可以为参数提供名称,但xy 不是Action/Func 泛型委托的参数名称。如果您有这样声明的委托:

    delegate void D(int p);
    
    //now
    D x = a => { };
    
    x(a: 1); //is illegal, since 'a' is not the name of the parameter but 'p'; so 
    x(p: 1) //is legal
    

    a 真的不能是那个参数名称,因为a 只是您的委托引用的当前方法(即匿名方法)签名的一部分。它实际上并不是原始代表签名的一部分。想想这个场景:

    D x = a => { };
    
    //and somewhere else
    x = b => { };
    
    //and yet again
    x = SomeMethod;
    
    // now should it be x(a: 1) or x(b: 1) or x(parameterNameOfMethodSomeMethod: 1)?
    

    只有p 在那里有意义。

    对于Action/Func,它们的声明如下:

    public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
    public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
    

    所以你看到参数命名正确吗?所以在这种情况下:

    Func<int, int, int> sum = delegate(int x, int y) { return x + y; };
    Action<string, DateTime> print =
        delegate(string message, DateTime datetime) { Console.WriteLine("{0} {1}", message, datetime); };
    
    //and you should be calling them like:
    
    Console.WriteLine(sum(arg1: 20, arg2: 40));
    print(arg2: DateTime.Now, arg1: "Hello"); //note the order change here
    

    当然在这种情况下它没有意义,因为您没有使用任何可选参数。

    【讨论】:

      最近更新 更多