开发者

C# Generic Type is boxed?

I executed the following code:

using System;
using System.Collections.Generic;

namespace TestReleaseAndDebug
{
    public class GClass<T1, T2>
    {
        public T1 Name { get; set; }      
        public T2 Age { get; set; }

        public void Display()
        {
            Console.WriteLine("Name: " + Name);           
            Console.WriteLine("Age: " + Age);
        }
    }

    class Program
    {        
        static void Main(string[] args)
        {
            GClass<string, int> person = new GClass<string, int>();
            person.Name = "RAM";         
            person.Age = 34;
            string name = "RAM";          
            int age = 34;

            Console.WriteLine("Name: " + name);         
            Console.WriteLine("Age: " + age);           
            person.Display();

            Console.Read();
        }
    }
}

I am having two local variables in the Main function they are name and age. I am printing them using console.writeline method. It prints without any problem. The IL of the main method is as shown below:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       90 (0x5a)
  .maxstack  2
  .locals init ([0] class TestReleaseAndDebug.GClass`2<string,int32> person,
           [1] string name,
           [2] int32 age)
  IL_0000:  nop
  IL_0001:  newobj     instance void class TestReleaseAndDebug.GClass`2<string,int32>::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldstr      "RAM"
  IL_000d:  callvirt   instance void class TestReleaseAndDebug.GClass`2<string,int32>::set_Name(!0)
  IL_0012:  nop
  IL_0013:  ldloc.0
  IL_0014:  ldc.i4.s   34
  IL_0016:  callvirt   instance void class TestReleaseAndDebug.GClass`2<string,int32>::set_Age(!1)
  IL_001b:  nop
  IL_001c:  ldstr      "RAM"
  IL_0021:  stloc.1
  IL_0022:  ldc.i4.s   34
  IL_0024:  stloc.2
  IL_0025:  ldstr      "Name: "
  IL_002a:  ldloc.1
  IL_002b:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0030:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0035:  nop
  IL_0036:  ldstr      "Age: "
  IL_003b:  ldloc.2
  IL_003c:  box        [mscorlib]System.Int32
  IL_0041:  call       string [mscorlib]System.String::Concat(object,
                                                              object)
  IL_0046:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_004b:  nop
  IL_004c:  ldloc.0
  IL_004d:  callvirt   instance void class TestReleaseAndDebug.GClass`2<string,int32>::Display()
  IL_0052:  nop
  IL_0053:  call       int32 [mscorlib]System.Console::Read()
  IL_0058:  pop
  IL_0059:  ret
} // end of method Program::Main

I have another Generic class 'GClass'. In the generic class I have two properties and one method (Display). In the Display method I display the two properties the same way I displayed the local variables in the Main method. The IL of the Generic Class Display method is given below:

.method public hidebysig instance void  Display() cil manage开发者_JAVA技巧d
{
  // Code size       56 (0x38)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Name: "
  IL_0006:  ldarg.0
  IL_0007:  call       instance !0 class TestReleaseAndDebug.GClass`2<!T1,!T2>::get_Name()
  IL_000c:  box        !T1
  IL_0011:  call       string [mscorlib]System.String::Concat(object,
                                                              object)
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001b:  nop
  IL_001c:  ldstr      "Age: "
  IL_0021:  ldarg.0
  IL_0022:  call       instance !1 class TestReleaseAndDebug.GClass`2<!T1,!T2>::get_Age()
  IL_0027:  box        !T2
  IL_002c:  call       string [mscorlib]System.String::Concat(object,
                                                              object)
  IL_0031:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0036:  nop
  IL_0037:  ret
} // end of method GClass`2::Display

I am passing 'string' as the type parameter to T1 and using this type to declare Name property. When the Name Property is displayed using Console.Writeline, it is boxing the Name ( IL_000c: box !T1). You can find this in IL.

Why boxing happens eventhough it is a string type?


This is so because compiler is not sure that both T1 and T2 would always be a reference type or value-type. so it puts them into Object by default for both cases when T1 or T2, either of them is a value-type or reference-type.

The type Object can act in duality. It can box-unbox for value-types and hold references to the instances of any sub-class types when it is a reference-type.

So in case when T1 is string, it is actually not boxing, it's holding the reference of the string instance because Object is the base class of the string type, in fact any .Net type.

and in case when T2 is int, it is simple boxing-unboxing.


The compiler must generate IL that can work across all generic types. The compiler can't know that you always instatiate GCClass with <string, int>. It has to cope with the eventuality that T1 is a value type.

However, I'd expect box on a reference type to be a no-op. The JIT generates different machine code from the Display method's IL for reference and value types. For reference types, I'd expect the box instruction to be eliminated.

If you're sure that T1 will never be a value type you can add a : class constraint to it, which will remove that box instruction.


Check out the CLI specification

In Partition III, section 4.1, about the box instruction :

If typeTok is a value type, the box instruction converts val to its boxed form. When typeTok is a non-nullable type (§1.8.2.4), this is done by creating a new object and copying the data from val into the newly allocated object. If it is a nullable type, this is done by inspecting val’s HasValue property; if it is false, a null reference is pushed onto the stack; otherwise, the result of boxing val’s Value property is pushed onto the stack. If typeTok is a reference type, the box instruction does nothing

So the boxing occurs only if the generic type parameter is actually a value type. If it's a reference type, this instruction has no effect.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜