- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在 IL 的一些实验中,我试图将程序集中的 callvirt
调用更改为 call
方法。基本上发生的事情是我有一个继承链,其中包含我正在调用的成员函数。
基本上调用与此类似:
((MyDerivedClass)myBaseObject).SomeCall();
或在 IL
castclass MyDerivedClass // ** 1
call SomeCall() // ** 2
基类定义SomeCall
为抽象方法,派生类实现。派生类是密封的。
我知道 callvirt
基本上等同于检查对象是否为 null,如果它不是使用 vtable 调用方法,如果是,则抛出异常 .在我的例子中,我知道它永远不会是 null
并且我知道这是我想要调用的实现。我理解为什么在这种情况下您通常需要 callvirt
。
也就是说,因为我知道该对象永远不会为 null,并且始终是派生类型的实例,所以我认为这不是问题:
调用
,因为我们确切地知道要调用哪个成员。不需要 vtable 查找。在我看来,编译器在某些情况下也可以推断出这是一件非常合理的事情。对于那些感兴趣的人,是的,callvirt
有速度损失,尽管它很小。
但是。 PEVerify 告诉我这是错误的。作为一个好 child ,我总是注意 PEVerify 告诉我的内容。那我在这里错过了什么?为什么更改此调用会导致不正确的程序集?
显然,创建一个最小测试用例并不是那么简单……到目前为止,我的运气并不好。
至于问题本身,我可以简单地在更大的程序中重现它:
[IL]: Error: [C:\tmp\emit\test.dll : NubiloSoft.Test::Value][offset 0x00000007] The 'this' parameter to the call must be the calling method's 'this' parameter.
值的IL代码:
L_0000: ldarg.0
L_0001: ldfld class NubiloSoft.Test SomeField
L_0006: ldarg.1
L_0007: call instance bool NubiloSoft.Test::Contains(int32)
字段的类型是NubiloSoft.Test
。
至于Contains
,在基类中是抽象的,在派生类中是重写的。正如您所期望的那样。当我删除“抽象基方法”+“覆盖”时,PEVerify 再次喜欢它。
为了重现我这样做的问题,到目前为止还没有在最小的测试用例中重现它:
public abstract class FooBase
{
public abstract void MyMethod();
}
// sealed doesn't seem to do anything...
public class FooDerived : FooBase
{
public override void MyMethod()
{
Console.WriteLine("Hello world!");
}
}
public class FooGenerator
{
static void Main(string[] args)
{
Type t = CreateClass();
object o = Activator.CreateInstance(t, new[] { new FooDerived() });
var meth = t.GetMethod("Caller");
meth.Invoke(o, new object[0]);
Console.ReadLine();
}
public static Type CreateClass()
{
// Create assembly
var assemblyName = new AssemblyName("testemit");
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.RunAndSave, @"c:\tmp");
// Create module
var moduleBuilder = assemblyBuilder.DefineDynamicModule("testemit", "test_emit.dll", false);
// Create type : IFoo
var typeBuilder = moduleBuilder.DefineType("TestClass", TypeAttributes.Public, typeof(object));
// Apparently we need a field to trigger the issue???
var field = typeBuilder.DefineField("MyObject", typeof(FooDerived), FieldAttributes.Public);
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.HasThis, new Type[] { typeof(FooDerived) });
// Generate the constructor IL.
ILGenerator gen = constructorBuilder.GetILGenerator();
// The constructor calls the constructor of Object
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
// Store the field
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Stfld, field);
// Return
gen.Emit(OpCodes.Ret);
// Add the 'Second' method
var mb = typeBuilder.DefineMethod("Caller",
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual |
MethodAttributes.Final | MethodAttributes.Public, CallingConventions.HasThis,
typeof(void), Type.EmptyTypes);
// Implement
gen = mb.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldfld, field);
gen.Emit(OpCodes.Call, typeof(FooDerived).GetMethod("MyMethod"));
gen.Emit(OpCodes.Ret);
Type result = typeBuilder.CreateType();
assemblyBuilder.Save("testemit.dll");
return result;
}
}
当你运行它并调用 peverify 时,它会告诉你代码没有错误...:-S
我不确定这里发生了什么......在我看来它非常相似。
最佳答案
我怀疑 this blog post是相关的。特别是:
Some consider this a violation of privacy through inheritence. Lots of code is written under the assumption that overriding a virtual method is sufficient to guarantee custom logic within gets called. Intuitively, this makes sense, and C# lulls you into this sense of security because it always emits calls to virtual methods as callvirts.
然后:
Late in Whidbey, some folks decided this is subtly strange enough that we at least don’t want partially trusted code to be doing it. That it’s even possible is often surprising to people. We resolved the mismatch between expectations and reality through the introduction of a new verification rule.
The rule restricts the manner in which callers can make non-virtual calls to virtual methods, specifically by only permitting it if the target method is being called on the caller’s ‘this’ pointer. This effectively allows an object to call up (or down, although that would be odd) its own type hierarchy.
换句话说,假设此更改是您正在谈论的内容(听起来像),规则是为了防止 IL 违反对虚拟方法调用方式的正常预期。
您可能想尝试在 MyDerivedClass
中制作 SomeCall
方法 sealed
... 那时它不再是虚拟的在 MyDerivedClass
类型的引用上调用 SomeCall
将 总是 调用相同的方法...对于 peverify 是否足够非虚拟是不过是另一回事:)
关于c# - 更改 callvirt 以在 IL 中调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31980792/
我试图了解 CLR 如何实现引用类型和多态性。我提到了 Don Box 的 Essential .Net Vol 1,它对校准大部分内容有很大帮助。但是当我尝试使用一些 IL 代码以更好地理解时,我被
CIL 指令“Call”和“Callvirt”有什么区别? 最佳答案 当运行时执行call指令时,它会调用一段确切的代码(方法)。毫无疑问它存在于何处。 IL 经过 JIT 处理后,调用站点生成的机器
是 il.EmitCall(OpCodes.Callvirt, getter, null); 等同于: 到 il.Emit(OpCodes.Callvirt,getter); 我不确定如何检查动态生成
如何确定一个方法是否需要使用“Call”或“Callvirt”来调用? 最佳答案 您可以一一遵循这些简单的规则来确定您应该使用哪一个: 方法是静态的吗?然后使用调用。 您正在调用的类型是值类型吗?然后
这个问题链接到How CLR calls methods correctly hidden by derived class when reference is stored in a base cl
在这种情况下,IL 并不总是对 virtual 方法使用 callvirt 指令: class MakeMeASandwich{ public override string ToString()
向某人解释虚拟调度很容易:每个对象都有一个指向表的指针作为其数据的一部分。类上有N个虚方法。每次调用特定方法时,我都会在对象到达时对其进行索引,并调用表中的第 i 个方法。每个实现方法 X() 的类都
在 IL 的一些实验中,我试图将程序集中的 callvirt 调用更改为 call 方法。基本上发生的事情是我有一个继承链,其中包含我正在调用的成员函数。 基本上调用与此类似: ((MyDerived
给定两种方法: static void M1(Person p) { if (p != null) { var p1 = p.N
对于下面的代码片段: struct Test { public override string ToString() { return ""; } } publ
我在其基类中标记为抽象的库类上调用属性集访问器。现在在运行时我 force应用程序针对另一个版本的库运行,其中类仅实现基类的底层接口(interface),但不是从它派生的。 有趣的是,.NET 将运
为什么编译器生成一个 callvirt 指令用于调用显式实现的接口(interface)方法和一个 call 用于调用 implicilty 在以下代码中实现了接口(interface)方法? 编译器
我有几个月前编写的这个框架,它生成一个类来调用此性能服务。框架的消费者使用方法创建一个接口(interface),使用属性进行注释,并调用一个工厂方法来创建他们可以用来调用此性能服务的接口(inter
我很想知道为什么会这样。请阅读下面的代码示例以及在每个部分下面的注释中发出的相应 IL: using System; class Program { static void Main()
我有一个类: public class SomeClass { public int I; public SomeClass(int input) { I = inpu
IL 提供了两种调用函数的语句,即 call 和 callvirt。 Call 用于调用非虚拟或静态函数或编译器不想对引用进行空检查的任何函数。 callvirt 用于调用虚函数,非虚函数也被调用,因
我有一个递归函数 emit : Map -> exp -> unit哪里il : ILGenerator对函数来说是全局的,并且 exp是一个判别联合,表示带有大小写 InstanceCall of
我是一名优秀的程序员,十分优秀!