Why it's not possible to do the following :
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");
case with only named parameters :
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");
case with only optional parameters:
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");
C# 7 now allows for 'local functions'. So instead of creating an Action<T>
or Func<T>
you can write a 'normal' method. This means that normal rules about default parameters apply.
So you can scope some piece of logic to be internal to a function without fighting with the delegate syntax.
It also works like a closure so you have access to local variables from the 'parent' method.
I added a pointless throwAnException
optional parameter below to Microsoft's example.
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;
}
}
As mentioned here -
Optional parameters are an attribute of a method or delegate parameter. When you call a signature (method or delegate) that has a known optional parameter at compile-time, the compiler will insert the optional parameter value at the callsite.
The runtime is not aware of optional parameters, so you can't make a delegate that inserts an optional parameter when it's called.
So, to use that you have to extract out concrete implementation(custom delegate) which is known at compile time and will replace the parameters at call site with optional parameters and named parameters can be used as well.
Declare custom delegate -
public delegate int FuncDelegate(int x, int y = 20);
Now you can use it in method body -
FuncDelegate sum = delegate(int x, int y) { return x + y; };
int result = sum (x : 20, y: 40 );
result = sum(20);
Also, only compile time constant can be used in default parameters list
.
But DateTime.Now is not a compile time constant
so that cannot be used as well for specifying optional value to your parameter.
So for Action part this will work -
public delegate void ActionDelegate(string message,
DateTime dateTime = default(DateTime));
Use delegate now here -
ActionDelegate print =
delegate(string message, DateTime dateTime)
{ Console.WriteLine(dateTime.ToString()); };
print(dateTime: DateTime.Now, message: "SomeThing");
You have an answer for the optional parameter part. Regarding the named parameter, its entirely possible to provide names for arguments, but just that x
and y
are not the parameter names for Action/Func
generic delegates. If you have a delegate declared like this:
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
really cant be that parameter name because a
is just a part of the signature of the current method your delegate references to (ie the anonymous method). It is not really part of the signature of the original delegate. Think about this scenario:
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)?
Only p
makes sense there.
In the case of Action/Func
they are declared like:
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);
So you see the parameter naming right? So in this case:
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
Of course its meaningless in this scenario since you're not using any optional parameter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With