11

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

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");
4

3 回答 3

9

正如这里提到的-

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

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

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

声明自定义委托 -

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");
于 2013-11-03T10:43:39.870 回答
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

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

于 2013-12-22T00:32:40.050 回答
1

C# 7 现在允许'local functions'。因此,您可以编写一个“普通”方法,而不是创建一个Action<T>or 。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;
    }
}
于 2019-01-30T20:04:17.733 回答