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,
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.
This approach is similar to Jared Par's answer. But I think !typeof(T).IsValueType
is 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.
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?
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;
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!)
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