Skip to content

Instantly share code, notes, and snippets.

@OlegKarasik
Last active January 18, 2024 13:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OlegKarasik/51e001122302740a2a011491dc1d0703 to your computer and use it in GitHub Desktop.
Save OlegKarasik/51e001122302740a2a011491dc1d0703 to your computer and use it in GitHub Desktop.
Code Tip: How to invoke delegate with arguments from dependency injection container?
// The explanation can be found here: https://wp.me/pa1cW1-2B
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace Code
{
public class InstanceClass
{
}
internal static class Program
{
private static object InvokeDelegate(
IServiceProvider provider,
Delegate @delegate)
{
var method = @delegate.GetMethodInfo();
var parameters = method.GetParameters();
// When delegate Method is static the delegate was created using
// Expression.Lambda or DynamicMethod
//
// (because compiler generated delegates are always instance methods).
if (method.IsStatic)
{
// If the Target isn't null Then we need to skip first parameter.
//
// (because DynamicInvoke would bind Target as first argument automatically).
if (@delegate.Target != null)
{
parameters = parameters.Skip(1).ToArray();
}
}
var arguments = parameters
.Select(pi => provider.GetRequiredService(pi.ParameterType))
.ToArray();
return @delegate.DynamicInvoke(arguments);
}
private static Task InvokeDelegateAsync(
IServiceProvider provider,
Delegate @delegate)
{
var method = @delegate.GetMethodInfo();
if (method.ReturnParameter != null)
{
if (typeof(Task).IsAssignableFrom(method.ReturnType))
{
return (Task) InvokeDelegate(provider, @delegate);
}
if (typeof(ValueTask) == method.ReturnType)
{
return ((ValueTask) InvokeDelegate(provider, @delegate)).AsTask();
}
if (method.ReturnType.IsGenericType)
{
if (typeof(ValueTask<>) == method.ReturnType.GetGenericTypeDefinition())
{
Expression<Func<Task>> expression = () => InvokeDelegateValueTaskAsync<int>(null, null);
var m = ((MethodCallExpression) expression.Body)
.Method
.GetGenericMethodDefinition()
.MakeGenericMethod(method.ReturnType.GetGenericArguments());
return (Task) m.Invoke(
null,
new object[]
{
provider,
@delegate
});
}
}
}
InvokeDelegate(provider, @delegate);
return Task.CompletedTask;
}
private static Task InvokeDelegateValueTaskAsync<T>(
IServiceProvider provider,
Delegate @delegate)
{
return ((ValueTask<T>) InvokeDelegate(provider, @delegate)).AsTask();
}
private static async Task Main(
string[] args)
{
var @object = new object();
var collection = new ServiceCollection();
collection.AddSingleton(typeof(object), @object);
var provider = collection.BuildServiceProvider();
var actionDelegate = new Action<object>(
o =>
{
Console.WriteLine("actionDelegate");
});
var functionDelegate = new Func<object, Task>(
o =>
{
Console.WriteLine("functionDelegate");
return Task.CompletedTask;
});
var functionGenericTaskDelegate = new Func<object, Task<int>>(
o =>
{
Console.WriteLine("functionGenericTaskDelegate");
var tcs = new TaskCompletionSource<int>();
tcs.SetResult(0);
return tcs.Task;
});
var functionValueTaskDelegate = new Func<object, ValueTask>(
o =>
{
Console.WriteLine("functionValueTaskDelegate");
return new ValueTask(Task.CompletedTask);
});
var functionValueTaskGenericDelegate = new Func<object, ValueTask<int>>(
o =>
{
Console.WriteLine("functionValueTaskGenericDelegate");
var tcs = new TaskCompletionSource<int>();
tcs.SetResult(0);
return new ValueTask<int>(tcs.Task);
});
var lambdaDelegate = Expression.Lambda(
((Expression<Action>) (() => Console.WriteLine("lambdaDelegate"))).Body,
Expression.Parameter(typeof(object), "o"))
.Compile();
var noOwnerStaticDynamicMethod = new DynamicMethod(
"NoOwnerStatic",
typeof(void),
new[]
{
typeof(object)
});
var noOwnerStaticDynamicMethodIl = noOwnerStaticDynamicMethod.GetILGenerator();
noOwnerStaticDynamicMethodIl.EmitWriteLine("noOwnerStaticDynamicMethod");
noOwnerStaticDynamicMethodIl.Emit(OpCodes.Ret);
var noOwnerStaticDynamicMethodDelegate = noOwnerStaticDynamicMethod.CreateDelegate(typeof(Action<object>));
var ownedStaticDynamicMethod = new DynamicMethod(
"OwnedStatic",
typeof(void),
new[]
{
typeof(object)
},
typeof(Program));
var ownedStaticDynamicMethodIl = ownedStaticDynamicMethod.GetILGenerator();
ownedStaticDynamicMethodIl.EmitWriteLine("ownedStaticDynamicMethod");
ownedStaticDynamicMethodIl.Emit(OpCodes.Ret);
var ownedStaticDynamicMethodDelegate = ownedStaticDynamicMethod.CreateDelegate(typeof(Action<object>));
var instanceDynamicMethod = new DynamicMethod(
"InstanceMethod",
typeof(void),
new[]
{
typeof(InstanceClass),
typeof(object)
},
typeof(InstanceClass));
var instanceDynamicMethodIl = instanceDynamicMethod.GetILGenerator();
instanceDynamicMethodIl.EmitWriteLine("instanceDynamicMethod");
instanceDynamicMethodIl.Emit(OpCodes.Ret);
var instanceDynamicMethodDelegate = instanceDynamicMethod.CreateDelegate(typeof(Action<object>), new InstanceClass());
Console.WriteLine("Synchronous");
Console.WriteLine();
InvokeDelegate(provider, actionDelegate);
InvokeDelegate(provider, lambdaDelegate);
InvokeDelegate(provider, noOwnerStaticDynamicMethodDelegate);
InvokeDelegate(provider, ownedStaticDynamicMethodDelegate);
InvokeDelegate(provider, instanceDynamicMethodDelegate);
Console.WriteLine();
Console.WriteLine("Asynchronous");
Console.WriteLine();
await InvokeDelegateAsync(provider, actionDelegate);
await InvokeDelegateAsync(provider, lambdaDelegate);
await InvokeDelegateAsync(provider, noOwnerStaticDynamicMethodDelegate);
await InvokeDelegateAsync(provider, ownedStaticDynamicMethodDelegate);
await InvokeDelegateAsync(provider, instanceDynamicMethodDelegate);
await InvokeDelegateAsync(provider, functionDelegate);
await InvokeDelegateAsync(provider, functionGenericTaskDelegate);
await InvokeDelegateAsync(provider, functionValueTaskDelegate);
await InvokeDelegateAsync(provider, functionValueTaskGenericDelegate);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment