使用可选参数和命名参数与Action和Func委托。

12

为什么以下操作是不可能的:

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 Tillapudi
1
@Sudhakar,OP想要定义一个带有可选参数(即具有默认值的参数)的ActionFunc - Mike Perrenoud
1
可能是Parameter Action<T1, T2, T3> in which T3 can be optional的重复问题。 - Sriram Sakthivel
1
@SriramSakthivel 这个问题只有一部分是与链接问题重复的。我认为我们最好将其保持开放状态。 - nawfal
3个回答

10
这里所提到的:

可选参数是方法或委托参数的属性。当你调用一个在编译时已知具有可选参数的签名(方法或委托)时,编译器将在调用点插入可选参数值。

运行时不知道可选参数,因此您不能创建一个在调用时插入可选参数的委托。

因此,要使用它,您必须提取出在编译时已知的具体实现(自定义委托),并将调用点处的参数替换为可选参数,也可以使用命名参数。
声明自定义委托 -
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);

此外,默认参数列表中只能使用编译时常量,但是DateTime.Now不是编译时常量,因此也不能用于指定参数的可选值。

因此,对于Action部分,以下方法可以正常工作 -

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委托。但它们没有起作用。 - Brahim Boulkriat
是的,我提到了可选参数只能在编译时用于方法和委托。Func 和 Action 更多地是在运行时而不是在编译时使用。 - Rohit Vats
有点讽刺的是,Action<T> 的定义是“封装具有四个参数且不返回值的方法。” - Simon_Weaver

4
你对于“可选参数”部分已经有了答案。关于“命名参数”,它完全可以为参数提供名称,但是Action/Func泛型委托的xy不是参数名称。如果你声明了这样的一个委托:
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

当然,在这种情况下,由于您没有使用任何可选参数,它是毫无意义的。

3
C# 7 现在支持'本地函数'。因此,您可以编写一个“普通”方法,而不是创建一个Action<T>Func<T>。这意味着默认参数的常规规则适用。
因此,您可以将某些逻辑范围限定为函数内部,而无需使用委托语法进行操作。
它也像闭包一样工作,因此您可以访问“父”方法中的局部变量。
下面我添加了一个无意义的throwAnException可选参数到Microsoft的示例中。
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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接