For example I have
private void test(Action<ValueTuple<string, int>> fn) { fn(("hello", 10)); } test(t => { var (s, i) = t; Console.WriteLine(s); Console.WriteLine(i); });
I would like to write something like this
private void test(Action<ValueTuple<string, int>> fn) { fn(("hello", 10)); } test((s,i) => { Console.WriteLine(s); Console.WriteLine(i); });
Is this possible with some proper notation?
The ' |= ' symbol is the bitwise OR assignment operator.
In mathematics, the tilde often represents approximation, especially when used in duplicate, and is sometimes called the "equivalency sign." In regular expressions, the tilde is used as an operator in pattern matching, and in C programming, it is used as a bitwise operator representing a unary negation (i.e., "bitwise ...
C operators are one of the features in C which has symbols that can be used to perform mathematical, relational, bitwise, conditional, or logical manipulations. The C programming language has a lot of built-in operators to perform various tasks as per the need of the program.
In C/C++, the # sign marks preprocessor directives. If you're not familiar with the preprocessor, it works as part of the compilation process, handling includes, macros, and more.
You can shorten it to:
void test( Action<ValueTuple<string, int>> fn) { fn(("hello", 10)); } test(((string s, int i) t) => { Console.WriteLine(t.s); Console.WriteLine(t.i); });
Hopefully, one day we might be able to splat the parameters from a tuple to the method invocation:
void test(Action<ValueTuple<string, int>> fn) { fn(@("hello", 10)); // <-- made up syntax } test((s, i) => { Console.WriteLine(s); Console.WriteLine(i); });
But not at the moment.
I. Examples of Action
/Func
delegates with distinct-args vs. single n-tuple arguments:
// 1. Action with 3 distinct 'int' parameters Action<int, int, int> ArgsAction = (i1, i2, i3) => i1 += i2 += i3; // 2. Func with 3 distinct 'int' parameters, returning 'long' Func<int, int, int, long> ArgsFunc = (i1, i2, i3) => (long)i1 + i2 + i3; // 3. Action with a single 3-tuple parameter Action<(int, int, int)> TupleAction = args => args.Item1 += args.Item2 += args.Item3; // 4. Action with a single 3-tuple parameter, returning 'long' Func<(int, int, int), long> TupleFunc = args => (long)args.Item1 + args.Item2 + args.Item3;
II. Demonstrate direct usage of the above examples
long r; // pass distinct params to multi-arg methods ArgsAction(1, 2, 3); // 1. r = ArgsFunc(1, 2, 3); // 2. // pass tuple to tuple-taking methods TupleAction((1, 2, 3)); // 3. r = TupleFunc((1, 2, 3)); // 4.
The examples in the next two sections invoke the delegates in their respective non-native argument forms. To delay the method call or to retain an adapted delegate for caching or delayed/multiple-call secenarios, see VI. and VII.
III. disperse ("splat") a tuple into multi-arg methods.
(1, 2, 3).Scatter(ArgsAction); // 1. r = (1, 2, 3).Scatter(ArgsFunc); // 2.
IV. pass distinct args into tuple-taking methods:
TupleAction.Gather(1, 2, 3); // 3. r = TupleFunc.Gather(1, 2, 3); // 4.
V. Extension methods Scatter
and Gather
used above in (III) and (IV):
// disperse n-tuple into Action arguments public static void Scatter<T0, T1>(in this (T0 i0, T1 i1) t, Action<T0, T1> a) => a(t.i0, t.i1); public static void Scatter<T0, T1, T2>(in this (T0 i0, T1 i1, T2 i2) t, Action<T0, T1, T2> a) => a(t.i0, t.i1, t.i2); public static void Scatter<T0, T1, T2, T3>(in this (T0 i0, T1 i1, T2 i2, T3 i3) t, Action<T0, T1, T2, T3> a) => a(t.i0, t.i1, t.i2, t.i3); // disperse n-tuple into Func arguments public static TResult Scatter<T0, T1, TResult>(in this (T0 i0, T1 i1) t, Func<T0, T1, TResult> f) => f(t.i0, t.i1); public static TResult Scatter<T0, T1, T2, TResult>(in this (T0 i0, T1 i1, T2 i2) t, Func<T0, T1, T2, TResult> f) => f(t.i0, t.i1, t.i2); public static TResult Scatter<T0, T1, T2, T3, TResult>(in this (T0 i0, T1 i1, T2 i2, T3 i3) t, Func<T0, T1, T2, T3, TResult> f) => f(t.i0, t.i1, t.i2, t.i3); // accumulate 'n' distinct args and pass into Action as an n-tuple public static void Gather<T0, T1>(this Action<(T0, T1)> a, T0 i0, T1 i1) => a((i0, i1)); public static void Gather<T0, T1, T2>(this Action<(T0, T1, T2)> a, T0 i0, T1 i1, T2 i2) => a((i0, i1, i2)); public static void Gather<T0, T1, T2, T3>(this Action<(T0, T1, T2, T3)> a, T0 i0, T1 i1, T2 i2, T3 i3) => a((i0, i1, i2, i3)); // accumulate 'n' distinct args and pass into Func as an n-tuple public static TResult Gather<T0, T1, TResult>(this Func<(T0, T1), TResult> f, T0 i0, T1 i1) => f((i0, i1)); public static TResult Gather<T0, T1, T2, TResult>(this Func<(T0, T1, T2), TResult> f, T0 i0, T1 i1, T2 i2) => f((i0, i1, i2)); public static TResult Gather<T0, T1, T2, T3, TResult>(this Func<(T0, T1, T2, T3), TResult> f, T0 i0, T1 i1, T2 i2, T3 i3) => f((i0, i1, i2, i3));
VI. Bonus round. If you plan to call a tuple- or distinct-arg-taking delegate multiple times in its alternate form, or if you're not ready to actually invoke it yet, you may wish to explicitly pre-convert the delegate from tuple-taking form to the equivalent distinct-args delegate, or vice-versa. You can cache the converted delegate for multiple or arbitrary later re-use.
var ga = ArgsAction.ToGathered(); // 1. // later... ga((1, 2, 3)); // ... ga((4, 5, 6)); var gf = ArgsFunc.ToGathered(); // 2. // later... r = gf((1, 2, 3)); // ... r = gf((4, 5, 6)); var sa = TupleAction.ToScattered(); // 3. // later... sa(1, 2, 3); // ... sa(4, 5, 6); var sf = TupleFunc.ToScattered(); // 4. // later... r = sf(1, 2, 3); // ... r = sf(4, 5, 6); // of course these approaches also supports in-situ usage: ArgsAction.ToGathered()((1, 2, 3)); // 1. r = ArgsFunc.ToGathered()((1, 2, 3)); // 2. TupleAction.ToScattered()(1, 2, 3); // 3. r = TupleFunc.ToScattered()(1, 2, 3); // 4.
VII. Extension methods for bonus examples shown in VI.
// convert tuple-taking Action delegate to distinct-args form public static Action<T0, T1> ToScattered<T0, T1>(this Action<(T0, T1)> a) => (i0, i1) => a((i0, i1)); public static Action<T0, T1, T2> ToScattered<T0, T1, T2>(this Action<(T0, T1, T2)> a) => (i0, i1, i2) => a((i0, i1, i2)); public static Action<T0, T1, T2, T3> ToScattered<T0, T1, T2, T3>(this Action<(T0, T1, T2, T3)> a) => (i0, i1, i2, i3) => a((i0, i1, i2, i3)); // convert tuple-taking Func delegate to its distinct-args form public static Func<T0, T1, TResult> ToScattered<T0, T1, TResult>(this Func<(T0, T1), TResult> f) => (i0, i1) => f((i0, i1)); public static Func<T0, T1, T2, TResult> ToScattered<T0, T1, T2, TResult>(this Func<(T0, T1, T2), TResult> f) => (i0, i1, i2) => f((i0, i1, i2)); public static Func<T0, T1, T2, T3, TResult> ToScattered<T0, T1, T2, T3, TResult>(this Func<(T0, T1, T2, T3), TResult> f) => (i0, i1, i2, i3) => f((i0, i1, i2, i3)); // convert distinct-args Action delegate to tuple-taking form public static Action<(T0, T1)> ToGathered<T0, T1>(this Action<T0, T1> a) => t => a(t.Item1, t.Item2); public static Action<(T0, T1, T2)> ToGathered<T0, T1, T2>(this Action<T0, T1, T2> a) => t => a(t.Item1, t.Item2, t.Item3); public static Action<(T0, T1, T2, T3)> ToGathered<T0, T1, T2, T3>(this Action<T0, T1, T2, T3> a) => t => a(t.Item1, t.Item2, t.Item3, t.Item4); // convert distinct-args Func delegate to its tuple-taking form public static Func<(T0, T1), TResult> ToGathered<T0, T1, TResult>(this Func<T0, T1, TResult> f) => t => f(t.Item1, t.Item2); public static Func<(T0, T1, T2), TResult> ToGathered<T0, T1, T2, TResult>(this Func<T0, T1, T2, TResult> f) => t => f(t.Item1, t.Item2, t.Item3); public static Func<(T0, T1, T2, T3), TResult> ToGathered<T0, T1, T2, T3, TResult>(this Func<T0, T1, T2, T3, TResult> f) => t => f(t.Item1, t.Item2, t.Item3, t.Item4);
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