我有一个代表方法的MethodInfo
对象,该方法以ref struct
为参数,该结构有一个ReadOnlySpan<byte>
字段。如何通过MethodInfo
对象调用该方法?我不能使用MethodInfo.Invoke
,因为Invoke
需要作为object?[]?
数组的参数,而带有ReadOnlySpan<byte>
字段的struct
不能转换为object
。
那么,我如何通过反射调用一个方法,并向它传递一个ref结构值,其中该结构有一个类型为ReadOnlySpan
的字段?
这可以通过MethodInfo.CreateDelegate()
非常简单地实现。
下面是一个使用通用重载之一的示例,从.NET5.0:开始可用
public ref struct MyRefStruct
{
private ReadOnlySpan<byte> myReadOnlySpan;
public MyRefStruct(ReadOnlySpan<byte> myReadOnlySpan) =>
this.myReadOnlySpan = myReadOnlySpan;
public void Print()
{
foreach (byte b in myReadOnlySpan)
{
Console.WriteLine(b);
}
}
}
public class MyClass
{
private void PrintMyRefStruct(MyRefStruct myRefStruct) => myRefStruct.Print();
}
delegate void MyDelegate(MyRefStruct myRefStruct);
var myClass = new MyClass();
var myRefStruct = new MyRefStruct(new ReadOnlySpan<byte>(new byte[] { 0, 1 }));
var methodInfo = typeof(MyClass).GetMethod(
"PrintMyRefStruct",
BindingFlags.Instance | BindingFlags.NonPublic
);
methodInfo.CreateDelegate<MyDelegate>(myClass)(myRefStruct);
由于您的struct
自然是一个ref struct
,它将存在于堆栈上(并且无法逃到托管堆(,因此您面临编译器的几个限制,并且需要克服一些障碍。
- ref结构不能是数组的元素类型
- ref结构不能是类或非ref结构的字段的声明类型
- ref结构无法实现接口
- ref结构不能装箱为System.ValueType或System.Object
- ref结构不能是类型参数。lambda表达式或局部函数无法捕获vA ref结构变量
- ref结构变量不能在异步方法中使用。但是,您可以在同步方法中使用ref结构变量,例如返回Task或Task的
- 在迭代器中不能使用ref结构变量
如您所见,ref struct
不能在heap
上装箱。这意味着您将无法将强制转换为object
并使用Invoke
。但是,您可以使用表达式树或relection.emit
当使用表达式树时,您需要将委托与Expression.Lambda
一起使用,因为ref struct
不能用作Func
中的类型参数。
给定
public readonly ref struct Bob
{
public Bob(ReadOnlySpan<byte> myProperty)
=> MyProperty = myProperty;
public ReadOnlySpan<byte> MyProperty { get; }
}
...
public static byte DoSomething(Bob bob)
=> bob.MyProperty[1]; // return something from the span ¯_(ツ)_/¯
delegate byte myDelegate(Bob asd);
...
var bytes = new byte[] {1, 2, 3};
var span = bytes.AsSpan();
var bob = new Bob(span);
用法
var method = typeof(SomeType).GetMethod("DoSomething");
var parameter = Expression.Parameter(typeof(Bob), "b");
var call = Expression.Call(method, parameter);
var expression = Expression.Lambda<myDelegate>(call, parameter);
var func = expression.Compile();
var result = func(bob);
Console.WriteLine(result);
结果
2
或者使用参数内修改器,您需要使用MakeByRefType
返回一个Type对象,该对象在作为参考参数
public static byte DoSomething(in Bob bob)
=> bob.MyProperty[1]; // return something from the span ¯_(ツ)_/¯
delegate byte myDelegate(in Bob asd);
...
var method = typeof(SomeType).GetMethod("DoSomething", new[] {typeof(Bob).MakeByRefType()});
var parameter = Expression.Parameter(typeof(Bob).MakeByRefType(), "b");
var call = Expression.Call(method, parameter);
var expression = Expression.Lambda<myDelegate>(call, parameter);
var func = expression.Compile();
var result = func(bob);
注意:这段代码不是完美表达的堡垒,但它应该可以工作。此外,这是为static
方法构建的,如果它不是,则需要传入一个实例