Reflection.Emit代码调用“base”。而不是“这个”。在布尔域上

发布于 2025-01-01 07:58:56 字数 2235 浏览 1 评论 0原文

我有以下 A 类。

public class A
{
    public string Name { get; set; }
}

我需要使用 Reflection.Emit 发出动态代理来覆盖 Equals。

// This class must be generated by Reflection.Emit.
public class AProxy : A
{
    private bool equalsHasBeenCalled;

    public override bool Equals(object obj)
    {
        if (this.equalsHasBeenCalled)
        {
            return base.Equals(obj);
        }

        this.equalsHasBeenCalled = true;

        return CaseInsensitiveComparer.Equals(this, obj); // Demo.
    }
}

然而,实际生成的代码(使用 Reflector 查看)是:

public class AProxy : A
{
    private bool equalsHasBeenCalled;

    public override bool Equals(object obj)
    {
        if (base.equalsHasBeenCalled)
        {
            return base.Equals(obj);
        }

        base.equalsHasBeenCalled = true;

        return CaseInsensitiveComparer.Equals(this, obj);
    }
}

..这当然会抛出 System.FieldAccessException (因为不存在这样的成员)。正确的方法是调用this.equalsHasBeenCalled(而不是base.equalsHasBeenCalled)。

我正在使用 Reflector 的 Reflection.Emit 插件来生成代码(field1 是“equalsHasBeenCalled”字段的 FieldInfo):

        // Writing body
        gen.Emit(OpCodes.Nop);

        // I suspect it has to be around here.
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldfld, field1);
        gen.Emit(OpCodes.Ldc_I4_0);

        gen.Emit(OpCodes.Ceq);
        gen.Emit(OpCodes.Stloc_1);
        gen.Emit(OpCodes.Ldloc_1);
        gen.Emit(OpCodes.Brtrue_S, label25);
        gen.Emit(OpCodes.Nop);
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method2);
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Br_S, label42);
        gen.MarkLabel(label25);

        // ..and probably here also?
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldc_I4_1);
        gen.Emit(OpCodes.Stfld, field1);

        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method3);
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Br_S, label42);
        gen.MarkLabel(label42);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Ret);

I have the following class A.

public class A
{
    public string Name { get; set; }
}

I need to emit a dynamic proxy using Reflection.Emit to override Equals.

// This class must be generated by Reflection.Emit.
public class AProxy : A
{
    private bool equalsHasBeenCalled;

    public override bool Equals(object obj)
    {
        if (this.equalsHasBeenCalled)
        {
            return base.Equals(obj);
        }

        this.equalsHasBeenCalled = true;

        return CaseInsensitiveComparer.Equals(this, obj); // Demo.
    }
}

However, the actual generated code (viewed with Reflector) is:

public class AProxy : A
{
    private bool equalsHasBeenCalled;

    public override bool Equals(object obj)
    {
        if (base.equalsHasBeenCalled)
        {
            return base.Equals(obj);
        }

        base.equalsHasBeenCalled = true;

        return CaseInsensitiveComparer.Equals(this, obj);
    }
}

..which of course throws a System.FieldAccessException (since no such member exists). The correct is to call this.equalsHasBeenCalled (not base.equalsHasBeenCalled).

I am using the Reflection.Emit add-in for Reflector to generate the code (field1 is the FieldInfo for the "equalsHasBeenCalled" field):

        // Writing body
        gen.Emit(OpCodes.Nop);

        // I suspect it has to be around here.
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldfld, field1);
        gen.Emit(OpCodes.Ldc_I4_0);

        gen.Emit(OpCodes.Ceq);
        gen.Emit(OpCodes.Stloc_1);
        gen.Emit(OpCodes.Ldloc_1);
        gen.Emit(OpCodes.Brtrue_S, label25);
        gen.Emit(OpCodes.Nop);
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method2);
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Br_S, label42);
        gen.MarkLabel(label25);

        // ..and probably here also?
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldc_I4_1);
        gen.Emit(OpCodes.Stfld, field1);

        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method3);
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Br_S, label42);
        gen.MarkLabel(label42);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Ret);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

痞味浪人 2025-01-08 07:58:56

为什么不用 C# 编写所需的内容,反编译为 IL,然后看看它是如何设置的?另外,如果您需要在项目中执行更多类似的操作,我建议您查看 Castle DynamicProxy。

Why dont you write what you need in C#, decompile to IL and see how it's set up? Also, if you need to do any more of things like this in your project, I suggest you look into Castle DynamicProxy.

情话难免假 2025-01-08 07:58:56

也许 this 关键字会触发 Reflection.Emit 加载项中的错误,使其生成错误的代码。尝试删除它,因为它实际上并没有做任何事情,没有其他 equalsHasBeenCalled 您需要使用 this 关键字来消除歧义。

Perhaps the this keyword is triggering a bug in your Reflection.Emit add-in to make it generate the wrong code. Try removing it as it is not really doing anything, there is no other equalsHasBeenCalled you need to disambiguate using the this keyword.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文