从非虚拟方法更改为虚拟方法可能会导致意外行为

没有名字的念头

我在第6章通过C#第四版阅读CLR:

如果将方法定义为非虚拟方法,则永远不要将来将其更改为虚拟方法。原因是因为某些编译器将通过使用call指令而不是callvirt指令来调用非虚拟方法。如果方法从非虚拟更改为虚拟,并且未重新编译引用代码,则将以非虚拟方式调用虚拟方法,从而导致应用程序产生不可预测的行为。如果引用代码是用C#编写的,那么这不是问题,因为C#通过使用callvirt调用所有实例方法。但是,如果引用代码是使用其他编程语言编写的,则可能会出现问题。

但我不太清楚可能会发生哪种不可预测的行为?您能否举个例子或解释作者指的是哪种意外行为?

迈克·兹博雷(Mike Zboray)

调用OpCode文档表明,以非虚拟方式调用虚拟方法是可以接受的。它将仅基于IL中的已编译类型而不是运行时类型信息来调用该方法。

但是,据我所知,如果您非虚拟地调用虚拟方法,该方法将无法通过验证。这是一个简短的测试程序,在该程序中,我们将动态发出用于调用方法(虚拟或非虚拟),编译并运行它的IL:

using System.Reflection;
using System.Reflection.Emit;

public class Program
{
    public static void Main()
    {
        // Base parameter, Base method info
        CreateAndInvokeMethod(false, new Base(), typeof(Base), typeof(Base).GetMethod("Test"));
        CreateAndInvokeMethod(true, new Base(), typeof(Base), typeof(Base).GetMethod("Test"));
        CreateAndInvokeMethod(false, new C(), typeof(Base), typeof(Base).GetMethod("Test"));
        CreateAndInvokeMethod(true, new C(), typeof(Base), typeof(Base).GetMethod("Test"));
        Console.WriteLine();

        // Base parameter, C method info
        CreateAndInvokeMethod(false, new Base(), typeof(Base), typeof(C).GetMethod("Test"));
        CreateAndInvokeMethod(true, new Base(), typeof(Base), typeof(C).GetMethod("Test"));
        CreateAndInvokeMethod(false, new C(), typeof(Base), typeof(C).GetMethod("Test"));
        CreateAndInvokeMethod(true, new C(), typeof(Base), typeof(C).GetMethod("Test"));
        Console.WriteLine();

        // C parameter, C method info
        CreateAndInvokeMethod(false, new C(), typeof(C), typeof(C).GetMethod("Test"));
        CreateAndInvokeMethod(true, new C(), typeof(C), typeof(C).GetMethod("Test"));
    }

    private static void CreateAndInvokeMethod(bool useVirtual, Base instance, Type parameterType, MethodInfo methodInfo)
    {
        var dynMethod = new DynamicMethod("test", typeof (string), 
            new Type[] { parameterType });
        var gen = dynMethod.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_0);
        OpCode code = useVirtual ? OpCodes.Callvirt : OpCodes.Call;
        gen.Emit(code, methodInfo);
        gen.Emit(OpCodes.Ret);
        string res;
        try
        {
            res = (string)dynMethod.Invoke(null, new object[] { instance });
        }
        catch (TargetInvocationException ex)
        {
            var e = ex.InnerException;
            res = string.Format("{0}: {1}", e.GetType(), e.Message);
        }

        Console.WriteLine("UseVirtual: {0}, Result: {1}", useVirtual, res);
    }   
}

public class Base
{
    public virtual string Test()
    {
        return "Base";
    }
}

public class C : Base
{
    public override string Test()
    {
        return "C";
    }
}

输出:

UseVirtual:否,结果:System.Security.VerificationException:操作可能会使运行时不稳定。
UseVirtual:正确,结果:基本
UseVirtual:False,结果:System.Security.VerificationException:操作可能会使运行时不稳定。
UseVirtual:正确,结果:C

UseVirtual:否,结果:System.Security.VerificationException:操作可能会使运行时不稳定。
UseVirtual:true,结果:System.Security.VerificationException:操作可能会使运行时不稳定。
UseVirtual:否,结果:System.Security.VerificationException:操作可能会使运行时不稳定。
UseVirtual:true,结果:System.Security.VerificationException:操作可能会使运行时不稳定。

UseVirtual:否,结果:System.Security.VerificationException:操作可能会使运行时不稳定。
UseVirtual:正确,结果:C

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Android Studio 2.3更新:警告:使用不兼容的插件进行注释处理:android-apt。这可能会导致意外行为

在 <form> 内嵌套 <button> 会导致意外行为

子集数据帧会导致意外行为

SwiftUI withAnimation导致意外行为

非虚拟的模拟方法

从片段着色器恢复像素数据会导致意外行为

Laravel 5:API路由+通配符路由会导致意外行为

在中间件中调度redux操作会导致意外行为

在列表上使用Filter(Negate(is.na),x)会导致意外行为

Python从另一个线程启动/停止线程会导致意外行为

实体框架向集合添加对象会导致意外行为

将变量的引用分配给指针会导致意外行为

React-可能由于将函数作为prop传递而导致意外行为

Qt C++“新建”操作可能会导致意外错误?

SimpleDateFormat宽大处理导致意外行为

WHERE IN(SELECT NonExistingColumnName)导致意外行为

Spring 数据 @ReadOnlyProperty 导致意外行为

图像最大宽度属性导致意外行为

媒体查询导致意外行为

Axios获取导致意外行为的API调用

Bash sort -nu 导致意外行为

在可为空的可选参数中使用C#7.1默认文字会导致意外行为

为什么在Windows中同时添加私有和公共DNS服务器会导致意外行为?

修复“您已在此页面上多次包含Google Maps API。这可能会导致意外错误。”

使用反射的非虚拟方法调用

“覆盖”非虚拟方法的正式术语

Objective-C中的非虚拟方法

使用 Moq 验证非虚拟方法

发出方法以覆盖非虚拟物品