Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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, meaning that GetType() and IsValueType don't reveal the difference between a boxed value and an unboxed value. For example, in the following LinqPad C# expressions, I have faith that o1 is boxed and i1 is not boxed, but I would like a way to test it in code, or, second best, a way to know FOR SURE when looking at any variable or value, even if its type is "dynamic" or "object," whether it's boxed or not boxed.

Any advice?

// boxed? -- no way to tell from these answers!
object o1 = 123;
o1.GetType().Dump("o1.GetType()");
o1.GetType().IsValueType.Dump("o1.GetType().IsValueType");

// not boxed? -- no way to tell from these answers!
int i1 = 123;
i1.GetType().Dump("i1.GetType()");
i1.GetType().IsValueType.Dump("i1.GetType().IsValueType");
like image 555
Reb.Cabin Avatar asked Apr 27 '11 15:04

Reb.Cabin


2 Answers

Try the following

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

By using a generic we allow the function to take into account both the type of the expression as viewed by the compiler and it's underlying value.

Console.WriteLine(IsBoxed(42));  // False Console.WriteLine(IsBoxed((object)42)); // True Console.WriteLine(IsBoxed((IComparable)42));  // True 

EDIT

A couple of people have asked for clarification on why this needs to be generic. And questioned why this is even needed at all, can't the developer just look at code and tell if a value is boxed? In an attempt to answer both those questions consider the following method signature

void Example<T>(T param1, object param2, ISomething param3) where T : ISomething {   object local1 = param1;   ISomething local2 = param1;   ... } 

In this scenario any of the provided parameters or locals could potentially represent boxed values and could just as easily not be. It's impossible to tell by casual inspection, only an examination of a combination of the runtime type and the reference by which the value is held can determine that.

like image 119
JaredPar Avatar answered Sep 26 '22 17:09

JaredPar


Well, let's use the trick ...

What do we know?

  • Value-type variable gets boxed again and again when assigned into reference-type variable
  • Reference-type variable will not get boxed again ...

So we will just check whether it gets boxed again (into another object) ... so we compare references

isReferenceType will be false here, because we compare 2 objects on heap (one boxed in surelyBoxed, one boxed just in call to ReferenceEquals):

int checkedVariable = 123;     //any type of variable can be used here
object surelyBoxed = checkedVariable;
bool isReferenceType = object.ReferenceEquals(surelyBoxed, checkedVariable);

isReferenceType will be true here, because we compare 1 object on heap to itself:

object checkedVariable = 123;     //any type of variable can be used here
object surelyBoxed = checkedVariable;
bool isReferenceType = object.ReferenceEquals(surelyBoxed, checkedVariable);

This works for ANY type, not just for int and object

To put it into well-usable method:

    public static bool IsReferenceType<T>(T input)
    {
        object surelyBoxed = input;
        return object.ReferenceEquals(surelyBoxed, input);
    }

This method can be easily used like this:

int i1 = 123;
object o1 = 123;
//...
bool i1Referential = IsReferenceType(i1);  //returns false
bool o1Referential = IsReferenceType(o1);  //returns true
like image 38
Marcel Toth Avatar answered Sep 24 '22 17:09

Marcel Toth