在 C# 中使用base
仅适用于直接基础。您无法访问 base-base 成员。
看起来其他人用关于它可以在伊利诺伊州做的答案打败了我。
但是,我认为我编写代码的方式有一些优势,所以无论如何我都会发布它。
我所做的不同之处在于使用表达式树,它使您能够使用 C# 编译器进行重载解析和泛型参数替换。
那东西很复杂,如果你能提供帮助,你不想自己复制它。在您的情况下,代码将像这样工作:
var del =
CreateNonVirtualCall<Program, BaseClass, Action<ThirdClass>>
(
x=>x.SayNo()
);
您可能希望将委托存储在只读静态字段中,以便您只需编译一次。
您需要指定 3 个通用参数:
所有者类型 - 如果您不使用“CreateNonVirtualCall”,这是您将调用代码的类。
基类 - 这是您要从中进行非虚拟调用的类
委托类型。这应该表示使用“this”参数的额外参数调用的方法的签名。可以消除这种情况,但需要在代码生成方法中进行更多工作。
该方法接受一个参数,一个表示调用的 lambda。它必须是一个电话,而且只有一个电话。如果你想扩展代码生成,你可以支持更复杂的东西。
为简单起见,lambda body 被限制为只能访问 lambda 参数,并且只能将它们直接传递给函数。如果您扩展方法主体中的代码生成以支持所有表达式类型,则可以删除此限制。但这需要一些工作。你可以对回来的代表做任何你想做的事情,所以限制并不是什么大不了的事。
需要注意的是,这段代码并不完美。它可以使用更多的验证,并且由于表达式树的限制,它不适用于“ref”或“out”参数。
我确实在示例案例中使用 void 方法、返回值的方法和泛型方法对其进行了测试,并且成功了。但是,我敢肯定,您会发现一些不起作用的边缘情况。
无论如何,这是 IL Gen 代码:
public static TDelegate CreateNonVirtCall<TOwner, TBase, TDelegate>(Expression<TDelegate> call) where TDelegate : class
{
if (! typeof(Delegate).IsAssignableFrom(typeof(TDelegate)))
{
throw new InvalidOperationException("TDelegate must be a delegate type.");
}
var body = call.Body as MethodCallExpression;
if (body.NodeType != ExpressionType.Call || body == null)
{
throw new ArgumentException("Expected a call expression", "call");
}
foreach (var arg in body.Arguments)
{
if (arg.NodeType != ExpressionType.Parameter)
{
//to support non lambda parameter arguments, you need to add support for compiling all expression types.
throw new ArgumentException("Expected a constant or parameter argument", "call");
}
}
if (body.Object != null && body.Object.NodeType != ExpressionType.Parameter)
{
//to support a non constant base, you have to implement support for compiling all expression types.
throw new ArgumentException("Expected a constant base expression", "call");
}
var paramMap = new Dictionary<string, int>();
int index = 0;
foreach (var item in call.Parameters)
{
paramMap.Add(item.Name, index++);
}
Type[] parameterTypes;
parameterTypes = call.Parameters.Select(p => p.Type).ToArray();
var m =
new DynamicMethod
(
"$something_unique",
body.Type,
parameterTypes,
typeof(TOwner)
);
var builder = m.GetILGenerator();
var callTarget = body.Method;
if (body.Object != null)
{
var paramIndex = paramMap[((ParameterExpression)body.Object).Name];
builder.Emit(OpCodes.Ldarg, paramIndex);
}
foreach (var item in body.Arguments)
{
var param = (ParameterExpression)item;
builder.Emit(OpCodes.Ldarg, paramMap[param.Name]);
}
builder.EmitCall(OpCodes.Call, FindBaseMethod(typeof(TBase), callTarget), null);
if (body.Type != typeof(void))
{
builder.Emit(OpCodes.Ret);
}
var obj = (object) m.CreateDelegate(typeof (TDelegate));
return obj as TDelegate;
}