如何使用堆中的类型对象找到结构实例的虚拟方法? [英] How does a struct instance's virtual method get located using its type object in heap?

查看:69
本文介绍了如何使用堆中的类型对象找到结构实例的虚拟方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是一本书中的代码示例,用于显示何时将值类型装箱:

below is a code example from a book to show when a value type will be boxed:

internal struct Point 
{
   private readonly Int32 m_x, m_y;
   public Point(Int32 x, Int32 y) {
      m_x = x;
      m_y = y;
   }
   
   //Override ToString method inherited from System.ValueType
   public override string ToString() {
      return String.Format("({0}, {1})", m_x.ToString(), m_y.ToString());
   }
}

class Program
{
    static void Main(string[] args) {
       Point p1 = new Point(10, 10);
       p1.ToString();       
    }
}

作者说:

在对ToString的调用中,不必将p1装箱.刚开始,您会认为p1必须装箱,因为 ToString 是从基本类型System.ValueType继承的虚拟方法.通常,要调用虚拟方法,CLR需要确定对象的类型才能找到类型的方法表.由于p1是未装箱的值类型,因此没有类型对象指针.但是,即时(JIT)编译器发现Point覆盖了ToString方法,并且它发出的代码直接(非虚拟地)调用ToString,而无需执行任何装箱操作.编译器知道,多态性不能在这里发挥作用,因为Point是一个值类型,并且无法从该类型派生任何类型来提供此虚拟方法的另一种实现.

In the call to ToString, p1 doesn’t have to be boxed. At first, you’d think that p1 would have to be boxed because ToString is a virtual method that is inherited from the base type, System.ValueType. Normally, to call a virtual method, the CLR needs to determine the object’s type in order to locate the type’s method table. Because p1 is an unboxed value type, there’s no type object pointer. However, the just-in-time (JIT) compiler sees that Point overrides the ToString method, and it emits code that calls ToString directly (nonvirtually) without having to do any boxing. The compiler knows that polymorphism can’t come into play here because Point is a value type, and no type can derive from it to provide another implementation of this virtual method.

我有点明白了,因为 Point 覆盖了 System.ValueType 中的 ToString ,CLR不需要检查类型对象以找到类型的方法表,编译器可以发出直接调用ToString的IL代码.足够公平.

I kind of get what it means, because Point overrides ToString from System.ValueType, CLR doesn't need to check the type object to locate the type’s method table, the compiler can emits IL code that calls ToString directly. Fair enough.

但是,假设 p1 也从 System.ValueType 调用 GetHashCode :

class Program
{
    static void Main(string[] args) {
       Point p1 = new Point(10, 10);
       p1.ToString();  
       p1.GetHashCode();     
    }
}

因为 Point 结构没有覆盖 System.ValueType 中的 GetHashCode(),所以编译器这次无法直接发出IL代码,因此CLR需要定位类型的方法表以查找 GetHashCode 方法,但是正如作者所说,p1是未装箱的值类型,因此没有类型对象指针,因此CLR如何查找 GetHashCode堆中 Point 结构的类型对象中的方法?

since Point struct doesn't override GetHashCode() from System.ValueType, then compiler cannot emit IL codes directly this time and CLR needs to location the type’s method table to look up GetHashCode method, but as the author says p1 is an unboxed value type, there’s no type object pointer, so how can the CLR look up the GetHashCode method in Point struct's type object in heap?

推荐答案

如果我们查看生成的MSIL,则会看到以下内容:

If we look at the generate MSIL, we see the following:

IL_0000:  ldloca.s    00 // p1
IL_0002:  ldc.i4.s    0A 
IL_0004:  ldc.i4.s    0A 
IL_0006:  call        System.Drawing.Point..ctor
IL_000B:  ldloca.s    00 // p1
IL_000D:  constrained. System.Drawing.Point
IL_0013:  callvirt    System.Object.ToString
IL_0018:  pop         
IL_0019:  ldloca.s    00 // p1
IL_001B:  constrained. System.Drawing.Point
IL_0021:  callvirt    System.Object.GetHashCode
IL_0026:  pop     

让我们查找 ECMA-335第III.2.1部分受到约束.:

受约束的前缀仅允许在 callvirt 指令上使用. ptr 的类型必须是指向 thisType 的托管指针(&).受约束的前缀旨在允许以统一的方式生成 callvirt 指令,而与 thisType 是值类型还是引用类型无关.

The constrained. prefix is permitted only on a callvirt instruction. The type of ptr must be a managed pointer (&) to thisType. The constrained prefix is designed to allow callvirt instructions to be made in a uniform way independent of whether thisType is a value type or a reference type.

如果 thisType 是值类型,并且 thisType 实现 method ,则
ptr 被原封不​​动地作为指向由 thisType

If thisType is a value type and thisType implements method then
ptr is passed unmodified as the ‘this’ pointer to a call of method implemented by thisType

如果 thisType 是值类型,并且 thisType 没有实现 method ,则
ptr 被取消引用,装箱并作为"this"指针传递给 method

If thisType is a value type and thisType does not implement method then
ptr is dereferenced, boxed, and passed as the ‘this’ pointer to the callvirt of method

仅当在 System.Object System.ValueType System.Enum 上定义方法且未覆盖方法时,才会发生最后一种情况通过 thisType .在这最后一种情况下,装箱会生成原始对象的副本,但是由于 System.Object System.ValueType System上的所有方法.Enum 不会修改对象的状态,无法检测到这一事实.

This last case can only occur when method was defined on System.Object, System.ValueType, or System.Enum and not overridden by thisType. In this last case, the boxing causes a copy of the original object to be made, however since all methods on System.Object, System.ValueType, and System.Enum do not modify the state of the object, this fact cannot be detected.

所以,是的,这确实会引起装箱,但仅当没有覆盖时才会出现,因为 System.Object 方法需要一个类,而不是值类型.但是,如果它被重写,则该方法的 this 指针必须是托管指针,与任何其他valuetype方法相同.

So, yes, this does cause boxing, but only when there is no override, because System.Object methods expect a class, not a valuetype. But if it is overridden, then the this pointer of the method must be a managed pointer, the same as any other valuetype method.

这篇关于如何使用堆中的类型对象找到结构实例的虚拟方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆