Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
379 views
in Technique[技术] by (71.8m points)

c# - How does a struct instance's virtual method get located using its type object in heap?

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();       
    }
}

and the author says:

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.

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.

But let's say p1 also calls GetHashCode from System.ValueType as:

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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     

Let's look up ECMA-335 Part III.2.1 on constrained.:

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.

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

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

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.

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...