Value Type Conversion in Dynamically Generated IL

后端 未结 2 507
耶瑟儿~
耶瑟儿~ 2021-02-08 06:00

Update
Over a year later, and I finally realized the cause of this behavior. Essentially, an object can\'t be unboxed to a different type

相关标签:
2条回答
  • 2021-02-08 06:33

    In order to unbox the value, you must first have boxed it, and for the unbox to not throw you must have converted the value to the type you unbox it to before you boxed it.

    However, as the type of the property setter is known, and you're dealing with value types, you shouldn't have to box/unbox at all:

    E.g. if you wanted to call a property setter of type Int32 with an Int64 argument, it would go something like this:

    // Int 64 argument value assumed on top of stack now
    conv.i4  // convert it to int32
    callvirt   ...
    
    0 讨论(0)
  • 2021-02-08 06:35

    I know this doesn't directly answer your question, but after having to maintain many different IL generation implementations, I've found better success in using Expression Trees.

    They're available as part of the DLR for .NET 2.0/3.5, or integrated directly in .NET 4.0.

    You can compile your expression tree to a lambda or event emit directly to a DynamicMethod.

    Ultimately, the underlying Expression Tree API generates IL using the same ILGenerator mechanism.

    P.S. When I'm debugging IL generation like this, I like to create a simple Console test application and Reflector the compiled code.
    For your problem, I tried the following:

    static class Program
    {
        static void Main(string[] args)
        {
            DoIt((byte) 0);
        }
    
        static void DoIt(object value)
        {
            Entity e = new Entity();
            e.Value = (int)value;
        }
    }
    
    public class Entity
    {
        public int Value { get; set; }
    }
    

    And the IL generated is:

    L_0000: nop 
    L_0001: newobj instance void ConsoleApplication2.Entity::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldarg.0 
    L_0009: unbox.any int32
    L_000e: callvirt instance void ConsoleApplication2.Entity::set_Value(int32)
    L_0013: nop 
    L_0014: ret 
    

    It's unboxing to the value type just like you do. Guess what? I get an invalid cast exception! So the problem isn't the IL you're generating. I'd recommend you try using it as an IConvertable:

    static void DoIt(object value)
    {
        Entity e = new Entity();
        e.Value = ((IConvertible) value).ToInt32(null);
    }
    
    L_0000: nop 
    L_0001: newobj instance void ConsoleApplication2.Entity::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldarg.0 
    L_0009: castclass [mscorlib]System.IConvertible
    L_000e: ldnull 
    L_000f: callvirt instance int32 [mscorlib]System.IConvertible::ToInt32(class [mscorlib]System.IFormatProvider)
    L_0014: callvirt instance void ConsoleApplication2.Entity::set_Value(int32)
    L_0019: nop 
    L_001a: ret 
    
    0 讨论(0)
提交回复
热议问题