Last active
January 18, 2024 13:28
-
-
Save OlegKarasik/51e001122302740a2a011491dc1d0703 to your computer and use it in GitHub Desktop.
Code Tip: How to invoke delegate with arguments from dependency injection container?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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