Consider the following method:
static void Invoke<TArg>(Action<TArg> action, TArg arg) => action(arg);
I know; it seems useless, but it's a simplification of the actual code.
Anyway, I need to call Invoke
from a method with by-reference parameters, passing a lambda for action
. I need some way to return values for the by-reference parameters from inside that lambda. Finally, the lambda must be static
(non-capturing).
I've come up with the following wacky scheme:
static unsafe void UpdateFooAndGetBar(ref object foo, out object bar) {
var args = (foo, bar: (object)null);
Invoke(
static pArgs => {
ref var args = ref Unsafe.AsRef<(object, object)>(pArgs.ToPointer());
args.Item1 = 123;
args.Item2 = "baz";
},
new IntPtr(Unsafe.AsPointer(ref args))
);
foo = args.foo;
bar = args.bar;
}
That seems to work, but Unsafe
makes me nervous. So, is my code legit? Is there a better way?
Let's assume we have a custom delegate that allows by-ref usage:
delegate void ActionByRef<T>(ref T value);
We can then use that in our Invoke
:
static void Invoke<T>(ActionByRef<T> callback, ref T args)
=> callback(ref args); // TODO any other logic
and consume it without any unmanaged pointers:
public static void UpdateFooAndGetBar(ref object foo, out object bar)
{
var args = (foo, bar: (object)null);
Invoke(
static (ref (object foo, object bar) args) => {
args.Item1 = 123;
args.Item2 = "baz";
},
ref args
);
foo = args.foo;
bar = args.bar;
}
You could also take this a step further by having a custom delegate that matches your exact needs, but that is less reusable:
delegate void RenameThis(ref object foo, out object bar);
//...
static void Invoke(RenameThis callback, ref object foo, out object bar)
=> callback(ref foo, out bar);
//...
public static void UpdateFooAndGetBar(ref object foo, out object bar)
{
Invoke(
static (ref object foo, out object bar) => {
foo = 123;
bar = "baz";
},
ref foo,
out bar
);
}