ソースコード: https://www.pastefile.com/4mzhyg
フォーマットのデリゲートを作成しようとしています:
public delegate TReturn MethodCallerR<TTarget, TReturn>(ref TTarget target, object[] args);
/// <summary>
/// Generates a strongly-typed open-instance delegate to invoke the specified method
/// </summary>
public static MethodCallerR<TTarget, TReturn> DelegateForCallR<TTarget, TReturn>(this MethodInfo method) {
int key = GetKey<TTarget, TReturn>(method, kMethodCallerName);
Delegate result;
if (cache.TryGetValue(key, out result))
return (MethodCallerR<TTarget, TReturn>)result;
return GenDelegateForMember<MethodCallerR<TTarget, TReturn>, MethodInfo>(
method, key, kMethodCallerName, GenMethodInvocationR<TTarget>,
typeof(TReturn), typeof(TTarget).MakeByRefType(), typeof(object[]));
}
弱い型関数:
public static MethodCallerR<object, object> DelegateForCallR(this MethodInfo method) {
return DelegateForCallR<object, object>(method);
}
委任作成者:
static TDelegate GenDelegateForMember<TDelegate, TMember>(TMember member, int key, string dynMethodName,
Action<TMember> generator, Type returnType, params Type[] paramTypes)
where TMember : MemberInfo
where TDelegate : class {
var dynMethod = new DynamicMethod(dynMethodName, returnType, paramTypes, true);
emit.il = dynMethod.GetILGenerator();
generator(member);
var result = dynMethod.CreateDelegate(typeof(TDelegate));
cache[key] = result;
return (TDelegate)(object)result;
}
および IL コード ジェネレーター:
static void GenMethodInvocationR<TTarget>(MethodInfo method) {
var weaklyTyped = typeof(TTarget) == typeof(object);
// push arguments in order to call method
var prams = method.GetParameters();
var imax = prams.Length;
for (int i = 0; i < imax; i++) {
emit.ldarg1() // stack<= paramsValuesArray[] //push array
.ldc_i4(i) // stack<= index push(index)
.ldelem_ref(); // stack[top]<=paramsValuesArray[i]
var param = prams[i];
var dataType = param.ParameterType;
if (dataType.IsByRef)
dataType = dataType.GetElementType();
emit.unbox_any(dataType);
emit.declocal(dataType);
emit.stloc(i);
}
if (!method.IsStatic)
{
var targetType = weaklyTyped ? method.DeclaringType : typeof(TTarget);
emit.ldarg0(); //stack[top]=target;
emit.ldind_ref();//stack[top]=ref target;
if (weaklyTyped)
emit.unbox_any(targetType); //stack[top]=(TargetType)target;
}
//load parms from local 'list' to evaluation 'steak'
for (int i = 0; i < imax; i++) {
var param = prams[i];
emit.ifbyref_ldloca_else_ldloc(i, param.ParameterType);
}
// perform the correct call (pushes the result)
emit.callorvirt(method);
//check of ref and out params and
for (int i = 0; i < prams.Length; i++) {
var paramType = prams[i].ParameterType;
if (paramType.IsByRef)
{
var byRefType = paramType.GetElementType();
emit.ldarg1() // stack<= paramsValuesArray[]
.ldc_i4(i) // stack<= i //push(index)
.ldloc(i); // stack<= list[i] //push local list element at 'i' on steak
if (byRefType.IsValueType)
emit.box(byRefType); // if ex. stack[top] =(object)stack[top]
emit.stelem_ref(); // // paramsValuesArray[i]= pop(stack[top]);
}
}
if (method.ReturnType == typeof(void))
emit.ldnull();
else if (weaklyTyped)
emit.ifvaluetype_box(method.ReturnType);
emit.ret();
}
私が使用しているスタックの例は、Set メソッドを使用した Vector3 です。
public struct Vector3{
public float x;
public float y;
public float z;
public Vector3(float x,float y,float z){
this.x=x;
this.y=y;
this.z=z;
}
public void Set(float x,float y,float z){
this.x=x;
this.y=y;
this.z=z;
}
}
だから私はやっている:
object vector3Obj=new Vector3(4,5,6);
MethodInfo method=typeof(Vector3).GetMethod("Set");
MethodCallerR<object,object> m = methodInfo.DelegateForCallR();
m(ref vector3Obj,new object[]{1f,2f,3f});
Console.Write(vector3.x);
実行中のデリゲートに到達することはありませんが、Delegate.CreateDelegate を呼び出すと失敗します。
次の行を参照してください: dynMethod.CreateDelegate(typeof(TDelegate));
エラーあり:
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) object:MC<> (object&,object[]): IL_004f: call 0x00000009 実際の IL コードが emit.call(method) でエラーを持っていることを参照していますが、ヘルパー関数を使用すると:
FastReflection.GenDebugAssembly<object>("my.dll",null,null,methodInfo,vector3Obj.GetType(),new Type[]{typeof(float),typeof(float),typeof(float)});
my.dll を生成し、ILSpy で開きます。同じ IL コードからのメソッドが問題なく生成されていることがわかります。
public static object MethodCallerR(ref object ptr, object[] array)
{
float num = (float)array[0];
float num2 = (float)array[1];
float num3 = (float)array[2];
((Vector3)ptr).Set(num, num2, num3);
return null;
}