How to test whether a value is boxed in C# / .NET?

后端 未结 9 1280
终归单人心
终归单人心 2020-12-13 00:32

I\'m looking for a way to write code that tests whether a value is boxed.

My preliminary investigations indicate that .NET goes out of its way to conceal the fact,

相关标签:
9条回答
  • 2020-12-13 01:07

    GetType() and IsValueType don't reveal the difference between a boxed value and an unboxed value.

    GetType is a sealed (non-virtual) method on System.Object. Calling this method on a value-type will definitely box it. Not even Nullable<T> is able to get around this - calling GetType on a nullable will return the underlying type if it has a value (boxed as the underlying type) or throw a NullReferenceException if it doesn't (boxed to null, can't dereference a null-reference).

    when looking at any variable or value, even if its type is "dynamic" or "object," whether it's boxed or not boxed.

    In general, if you have an expression that "holds" a value-type, the value of that expression will be a reference to a box unless the expression's compile-time type is of the value-type itself (generics are slightly more complicated). Common reference-types that can hold references to boxed structures are object, dynamic and interface-types.

    0 讨论(0)
  • 2020-12-13 01:15

    This approach is similar to Jared Par's answer. But I think !typeof(T).IsValueTypeis cleaner than enumerating all types which could contain a boxed value.

    public static bool IsBoxed<T>(T value)
    {
        return !typeof(T).IsValueType && (value != null) && value.GetType().IsValueType;
    }
    

    Unlike Jared's code this will handle the case where T is System.ValueType correctly.

    Another subtle point is that value.GetType().IsValueType comes after !typeof(T).IsValueType since otherwise GetType() would create a temporary boxed copy of the value.

    0 讨论(0)
  • 2020-12-13 01:18

    Here are some simple helper methods to check if a variable is a boxed integer:

    public static bool IsBoxed(object item)
    {
        return true;
    }
    
    public static bool IsBoxed<T>(T item) where T : struct
    {
        return false;
    }
    

    Just call IsBoxed(...) on your variable:

    IsBoxed(o1) // evaluates to true
    IsBoxed(i1) // evaluates to false
    

    This accomplishes nothing, of course. Why exactly do you need to know if a value is boxed or not?

    0 讨论(0)
  • 2020-12-13 01:20

    I think actually the question is kind of misstated. Isn't the question actually, "How can I tell if an object is a box for another type?"

    With reference to Allon's comment, if you have an object of type Object and the object is a primitive value type, it's a box. I'm not certain this is 100% correct, but (similar to Allon's implementation):

    // Assume there is some object o.
    bool isBoxed = o.GetType().IsPrimitive;
    
    0 讨论(0)
  • 2020-12-13 01:22

    Similar to Allon's answer, but should return the correct answer for any type without generating a compile-time error:

    int i = 123;
    Console.WriteLine(IsBoxed(i));    // false
    
    object o = 123;
    Console.WriteLine(IsBoxed(o));    // true
    
    IComparable c = 123;
    Console.WriteLine(IsBoxed(c));    // true
    
    ValueType v = 123;
    Console.WriteLine(IsBoxed(v));    // true
    
    int? n1 = 123;
    Console.WriteLine(IsBoxed(n1));    // false
    int? n2 = null;
    Console.WriteLine(IsBoxed(n2));    // false
    
    string s1 = "foo";
    Console.WriteLine(IsBoxed(s1));    // false
    string s2 = null;
    Console.WriteLine(IsBoxed(s2));    // false
    
    // ...
    
    public static bool IsBoxed<T>(T item)
    {
        return (item != null) && (default(T) == null) && item.GetType().IsValueType;
    }
    
    public static bool IsBoxed<T>(T? item) where T : struct
    {
        return false;
    }
    

    (Although you could make the argument that the possible compile-time error caused by Allon's code is a feature, not a bug: if you hit a compile-time error then you're definitely not dealing with an unboxed value type!)

    0 讨论(0)
  • 2020-12-13 01:25

    I'm not sure if this will be relevant to anyone, but since I encountered this post because boxing was actually impacting my very dynamic mapping.

    Sigil proivdes a fantastic UnBoxAny method

    Assuming you have the following:

    public class Report { public decimal Total { get; set; } }

    new Dictionary<string, object> { { "Total", 5m} }

    So the decimal value is boxed.

    var totalProperty = typeof(Report).GetProperty("Total");
    
    var value = emit.DeclareLocal<object>();
    
    //invoke TryGetValue on dictionary to populate local 'value'*
                                                        //stack: [bool returned-TryGetValue]
    //either Pop() or use in If/Else to consume value **
                                                        //stack: 
    //load the Report instance to the top of the stack 
    //(or create a new Report)
                                                        //stack: [report]
    emit.LoadLocal(value);                              //stack: [report] [object value]
    emit.UnboxAny(totalProperty.PropertyType);          //stack: [report] [decimal value]
    
    //setter has signature "void (this Report, decimal)" 
    //so it consumes two values off the stack and pushes nothing
    
    emit.CallVirtual(totalProperty.SetMethod);          //stack: 
    

    * invoke TryGetValue

    ** use in If/Else

    0 讨论(0)
提交回复
热议问题