Proposal: Nicer delegates #1426
Replies: 3 comments
-
How exactly would this be implemented? For point 1 the compiler could theoretically just emit a null-check, but I would argue that this is definitely not the appropriate nor expected behavior. In most cases the lack of a value would indicate an invalid state and the program should throw. If it's not a problem, the null propagation operator makes a combined null-check and invocation pretty easy. Point 2 is interesting but I don't know that there's much that the compiler can do here. Multicast delegates are a part of the CLR, as is their behavior. The compiler could potentially unwrap the invocation list of a delegate and invoke them individually, collecting their results. IIRC there'd be no way to write a general purpose helper method to accomplish this given that the signature of each delegate can be different and they are nominally typed. I do think that if considered that it should have a special syntax indicating what the compiler should do: string result = converters(42); // normal delegate invocation
IEnumerable<string> results = converters*(42); // multicast delegate invocation
// logically equivalent to
Delegate[] $list = converters.GetInvocationList();
List<string> results = new List<string>($list.Length);
for (int i = 0; i < $list.Length; i++) {
results.Add($list[i].Invoke(42);
} |
Beta Was this translation helpful? Give feedback.
-
For point 1, I think being explicit with null handling is better. Things to consider:
For point 2, multicast delegates that return values are not used often and I think they are not a good feature and so their use should not be encouraged by adding new features for them. If you want to have a collection of delegates, then I think it's best to be explicit about it and have e.g. If you still want to use multicast delegates, you could write a helper method that does what you want: static IEnumerable<string> InvokeAll(this RegularDelegate d, int x)
{
foreach (RegularDelegate innerDelegate in d.GetInvocationList())
{
yield return innerDelegate(x);
}
} It's true that you have to write that method for each delegate type, but that's not going to be that bad if you use the |
Beta Was this translation helpful? Give feedback.
-
If it saves anyone some time, I generated all the necessary extension methods for the public static class FuncExtensions
{
public static IEnumerable<TResult> InvokeAll<TResult>(this Func<TResult> f)
=> f.GetInvocationList().Select(i => ((Func<TResult>)i)());
public static IEnumerable<TResult> InvokeAll<T1, TResult>(this Func<T1, TResult> f, T1 t1)
=> f.GetInvocationList().Select(i => ((Func<T1, TResult>)i)(t1));
public static IEnumerable<TResult> InvokeAll<T1, T2, TResult>(this Func<T1, T2, TResult> f, T1 t1, T2 t2)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, TResult>)i)(t1, t2));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> f, T1 t1, T2 t2, T3 t3)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, TResult>)i)(t1, t2, t3));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, TResult>(this Func<T1, T2, T3, T4, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, TResult>)i)(t1, t2, t3, t4));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, TResult>(this Func<T1, T2, T3, T4, T5, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, TResult>)i)(t1, t2, t3, t4, t5));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, TResult>(this Func<T1, T2, T3, T4, T5, T6, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, TResult>)i)(t1, t2, t3, t4, t5, t6));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, TResult>)i)(t1, t2, t3, t4, t5, t6, t7));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15));
public static IEnumerable<TResult> InvokeAll<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>(this Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15, T16 t16)
=> f.GetInvocationList().Select(i => ((Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>)i)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16));
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
(Forgive me if this isn't formatted correctly or is missing required information. I looked around and didn't see any relevant templates or such. I've also looked for issues regarding this idea and did not find any)
Issues
There are two issues I've noticed with delegates that seem like they could be relatively easy to fix:
Demonstration of proposal
By way of example, let's start with a simple program using the current delegates:
Result is:
Problem 2 can be handled by having the nicer delegates return a collection:
Result is:
Problem 1 can be partly solved by making the null check on the delegate implicit and automatic:
(Results are the same as last example).
Then we can remove the remaining null check by having the compiler automatically return an empty collection if the delegate is
null
:In particular, the first instance of
var results = converters(42);
is replaced by the compiler with something like:In the second instance, the compiler should be able to detect that
converters
can't be null and so leave it as-is.Discussion
I think the solution presented should be relatively simple compared to many other language features, as it's largely just a slight modification of the existing
delegate
usage. The three biggest problems or hurdles I see with implementing this proposal are:Beta Was this translation helpful? Give feedback.
All reactions