ObjectMethodExecutor Class
Provides a unified way to invoke synchronous and asynchronous methods via reflection.
[RequiresDynamicCode("ObjectMethodExecutor compiles expression trees and may construct generic types at runtime.")]
[RequiresUnreferencedCode("ObjectMethodExecutor performs reflection on arbitrary types.")]
public sealed class ObjectMethodExecutorRemarks
This class creates optimized delegates for method invocation, avoiding repeated reflection overhead. It supports both synchronous and asynchronous methods, including those returning Task, ValueTask, or any custom awaitable type that implements the awaitable pattern.
Example
Basic usage with synchronous and asynchronous methods:
// Synchronous method
var replaceMethod = typeof(string).GetMethod(nameof(string.Replace), [typeof(string), typeof(string)])!;
var replaceExecutor = ObjectMethodExecutor.Create(replaceMethod);
Assert.Equal("Hello Universe", await InvokeAsync(replaceExecutor, "Hello World", ["World", "Universe"]));
// Asynchronous method
var readToEndMethod = typeof(StringReader).GetMethod(nameof(StringReader.ReadToEndAsync), Type.EmptyTypes)!;
var readToEndExecutor = ObjectMethodExecutor.Create(readToEndMethod);
using var reader = new StringReader("Hello");
Assert.Equal("Hello", await InvokeAsync(readToEndExecutor, reader, []));
static async Task<object?> InvokeAsync(ObjectMethodExecutor executor, object instance, object?[] parameters)
{
if (executor.IsMethodAsync)
{
return await executor.ExecuteAsync(instance, parameters);
}
else
{
return executor.Execute(instance, parameters);
}
}Properties
| Name | Description |
|---|---|
| IsMethodAsync | Gets a value indicating whether the method returns an awaitable type. |
IsMethodAsync
ObjectMethodExecutor.csMethods
| Name | Description |
|---|---|
| Create(MethodInfo) | Creates an executor for the specified method. |
| Create(MethodInfo, object?[]?) | Creates an executor for the specified method with parameter default values. |
| Execute(object?, object?[]?) | Executes the configured method on target. This can be used whether or not the configured method is asynchronous. |
| ExecuteAsync(object?, object?[]?) | Executes the configured method on target. |
| GetDefaultValueForParameter(int) | Gets the default value supplied for the parameter at index. |
Create overloads
Create(MethodInfo)
ObjectMethodExecutor.csCreates an executor for the specified method.
public static ObjectMethodExecutor Create(MethodInfo methodInfo)Parameters
methodInfoMethodInfo- The method to be invoked.
Returns
ObjectMethodExecutor An ObjectMethodExecutor that can invoke the specified method.
Create(MethodInfo, object?[]?)
ObjectMethodExecutor.csCreates an executor for the specified method with parameter default values.
public static ObjectMethodExecutor Create(MethodInfo methodInfo, object?[]? parameterDefaultValues)Parameters
methodInfoMethodInfo- The method to be invoked.
parameterDefaultValuesobject?[]?- The default values for the method parameters, readable afterwards through
ObjectMethodExecutor.GetDefaultValueForParameter. Must contain exactly one entry per parameter.
Returns
ObjectMethodExecutor An ObjectMethodExecutor that can invoke the specified method.
Execute(object?, object?[]?)
ObjectMethodExecutor.csExecutes the configured method on target. This can be used whether or not the configured method is asynchronous.
Parameters
targetobject?- The object whose method is to be executed.
parametersobject?[]?- Parameters to pass to the method.
Returns
object? The method return value.
Exceptions
ArgumentExceptionparametersdoes not contain one entry per method parameter.
Remarks
Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than ExecuteAsync if you know at compile time what the return type is, because then you can directly "await" that value (via a cast), and then the generated code will be able to reference the resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated code will have to treat the resulting awaitable as a boxed object, because it doesn't know at compile time what type it would be.
ExecuteAsync(object?, object?[]?)
ObjectMethodExecutor.csExecutes the configured method on target.
public ObjectMethodExecutorAwaitable ExecuteAsync(object? target, object?[]? parameters)Parameters
targetobject?- The object whose method is to be executed.
parametersobject?[]?- Parameters to pass to the method.
Returns
ObjectMethodExecutorAwaitable An object that you can "await" to get the method return value.
Exceptions
ArgumentExceptionparametersdoes not contain one entry per method parameter.InvalidOperationException- The configured method is an
async voidmethod, which cannot be awaited. UseObjectMethodExecutor.Executeinstead.
Remarks
If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync, which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations as compared with using Execute and then using "await" on the result value typecasted to the known awaitable type. The possible extra heap allocations are for: 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally it's a reference type, and you normally create a new instance per call). 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance of it, and if it is, it will have to be boxed so the calling code can reference it as an object). 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling code doesn't know what type it's going to be).
GetDefaultValueForParameter(int)
ObjectMethodExecutor.csGets the default value supplied for the parameter at index.
Parameters
indexint- The zero-based index of the parameter.
Returns
object? The default value supplied for that parameter when the executor was created.
Exceptions
InvalidOperationException- No parameter default values were supplied to
ObjectMethodExecutor.Create. ArgumentOutOfRangeExceptionindexis negative or not less than the number of parameters.
Remarks
Callers use this to build the parameters array passed to ObjectMethodExecutor.Execute or ObjectMethodExecutor.ExecuteAsync when they have no value of their own for a parameter. The executor itself never consults these values.