30

At the run-time I get boxed instance of some type. How to unbox it to underlying type?

Object obj;
String variable = "Some text";

obj = variable // boxing;

// explicit unboxing, because we know the type of variable at compile time.

var x = (String)obj     

// Now let's pretend that we don't know the type of underlying object at compile time. 

Type desiredType = obj.GetType(); // But we can figure out.

//And now the question. 
//How to express something like this:

var y = (desiredType)obj; //Need to get unboxed instance of initial variable here; 
Paul Kyrejto
  • 1,285
  • 3
  • 12
  • 19
  • 10
    `String` is a reference type, so no boxing occur when converting to `object` – Ilya Ivanov Mar 22 '13 at 07:42
  • _Value type objects have two representations: an unboxed form and a boxed form. Reference types (like `string`) are always in a boxed form._ – Soner Gönül Mar 22 '13 at 07:44
  • 3
    Well maybe bad example with the string. But never the less how to do it? – Paul Kyrejto Mar 22 '13 at 07:44
  • Side note: you need much more interesting construct to store value of not-know-at-compile-time value type than `var y = (desiredType)obj;` – Alexei Levenkov Mar 22 '13 at 07:47
  • That's why I'm asking. Just wanted to illustrate what I need :) – Paul Kyrejto Mar 22 '13 at 07:52
  • 4
    What is the purpose here? what are you trying to do, that you think you will be able to do with `y`, but which you couldn't previously do with `obj` ? – Marc Gravell Mar 22 '13 at 07:53
  • `Activator.CreateInstance(currentProperty.ReflectedType);` returns object. But I need to iterate its properties via reflection for instance. – Paul Kyrejto Mar 22 '13 at 08:03
  • And how it relates to boxing and unboxing? GetType() will give you correct type and reflection methods take object as argument anyway... – Alexei Levenkov Mar 22 '13 at 08:05
  • 2
    @PaulKyrejto: You can still do that: `var instance = Activator.CreateInstance(currentProperty.ReflectedType); instance.GetType().GetProperties().Select(x => x.GetValue(instance, null));` <- This is an example that would give you the values of all properties of the new instance. – Daniel Hilgarth Mar 22 '13 at 08:06
  • Side note: Paul, I'd recommend to keep your question as is (and consider to accept Marc's answer) and than ask separate question if you need to. I believe you actually used your own meaning for "unboxing" which triggered interesting answers/comments - consider reading on it at [MSDN:Boxing and Unboxing](http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx). – Alexei Levenkov Mar 22 '13 at 16:13
  • If you use dynamic instead of var, it should work – TruthOf42 Aug 17 '16 at 18:46
  • Can you elaborate on what you're trying to do with the unboxed object? If you're trying to call a method, then you should casting to an Interface. – Vlad274 Dec 13 '16 at 22:03
  • Related to that question: If you replace the assignment in your example by `var variable = new {value="Some text"};` and assign it to `obj` you're boxing an anonymous type. How to convert that back? **N.B.** Linqpad shows the type as `<>f__AnonymousType0\`1[System.String]`. – Matt Dec 14 '16 at 16:50

8 Answers8

25

If you don't know the type at compile time, then you can't unbox because you have nowhere to put it - all you can do is store it in an object, which is: boxed.

The same also applies to reference-types like string: you can't cast it to the right type if you don't know the type at compile time: you have nowhere to put it.

You can special-case a few types, for example:

if(obj is int) {
    int i = (int)obj;
    ...
} ...

Another trick that is sometimes (not often) helpful is to switch into generics; then instead of talking in terms of object you are talking in terms of T. This has... limited use though. The easiest way to do that is via dynamic, for example:

dynamic obj = ...
Foo(obj);
...
Foo<T>(T val) { ... code with T ... }

you can also add special cases to that appreach:

Foo(string val) { ... code with string ...}
Foo(int val) { ... code with int ...}

However, frankly I suggest it may be better to look hard at what you are trying to do.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • +1. One will need to construct generic type at runtime obviously. – Alexei Levenkov Mar 22 '13 at 07:52
  • @AlexeiLevenkov why? frankly I don't see much utility in what the OP is trying to do here... – Marc Gravell Mar 22 '13 at 07:52
  • +1. Thanks Marc, seeing all these `Convert.ChangeType` answers made me wonder: "How can ChangeType return an unboxed value in an `object`?" – ybo Mar 22 '13 at 07:53
  • Neither do I... But if one wants something like `List` and add elements to it than such List will need to be constructed based on type of elements... But maybe I misunderstood what you meant about "talking in terms of `T`"... – Alexei Levenkov Mar 22 '13 at 07:56
  • I believe you can unbox to unknown type please check the work around I made in my answer. – Mahmoud Fayez Mar 22 '13 at 08:05
  • Just one tiny note - in OPs question he stated `How to unbox...`, but when you use genetics no boxing occur in the first place. Anyway - great answer – Ilya Ivanov Mar 22 '13 at 08:09
  • @IlyaIvanov I was illustrating starting with an `object` and then unboxing *via* generics; and boxing can still happen in generics, depending on what you are doing - for example, an `object` method (such as `ToString()` or `GetType()`) will cause boxing of a generic if the method isn't overridden by the value-type (of course, for `GetType()` it ***cannot*** be overridden). constrained-call (the hybrid that sits between static-call and virtual-call) can only do so much. – Marc Gravell Mar 22 '13 at 08:15
  • @MarcGravell totally agree, I first wrote, that no boxing occur in simple cases or by default, i.e. passing `int` to `Func(T v)`. Sure you can box any value type in millions of different ways with or without generics – Ilya Ivanov Mar 22 '13 at 08:19
10

Now lets suppose, that real boxing occur:

int v = 5;

object o = v; //boxed 

Type type = o.GetType(); //will return typeof(int)

int convertedBack = (int)Convert.ChangeType(o, type);

Console.WriteLine (convertedBack); //prints 5

Remark, if you substitute:

object convertedBack = Convert.ChangeType(o, type);

Console.WriteLine (convertedBack); //it still prints 5
Console.WriteLine (o); //it even print 5 here

The reason is that underlying object is still int. I've just used this example to show you, that boxing is irrelevant here. You need to rely on some abstraction in your operations and if you want to cast to int dynamically, what reference type do you wan to use.

Ilya Ivanov
  • 23,148
  • 4
  • 64
  • 90
  • 1
    OK but you've used casting to int here. What do i need is smth like `var convertedback = (type)Convert.ChangeType(o, type);` – Paul Kyrejto Mar 22 '13 at 07:49
  • 3
    @PaulKyrejto syntax issues aside: in what *possible* way would that help you? What could you now do with `convertedback` that you couldn't do when it was `object` ? – Marc Gravell Mar 22 '13 at 07:52
  • 2
    @MarcGravell Next Q will be _"how to find all the things I can do with a thing that I don't know the type of?"_... – Grant Thomas Mar 22 '13 at 08:19
  • @MarcGravell There are certainly refactoring reasons for doing so. Instead of having a bunch of if statements or a switch, you could just do overloaded methods which is a much cleaner way to code. – Serj Sagan Jun 19 '15 at 19:11
5

In such cases I'm going to use the strategy pattern by using a Dictionary<Type, Action<object>>:

internal class Program
{
    private static void Main(string[] args)
    {
        var something = new Something();

        something.ComputeValue(13);
        something.ComputeValue(DateTime.Now);
        something.ComputeValue(DayOfWeek.Monday);

        Console.ReadKey();
    }
}

internal class Something
{
    private static Dictionary<Type, Action<object>> _Strategies;

    static Something()
    {
        // Prepare all available strategies.
        _Strategies = new Dictionary<Type, Action<object>>();
        _Strategies.Add(typeof(int), ComputeInteger);
        _Strategies.Add(typeof(DateTime), ComputeDateTime);
    }

    public void ComputeValue(object value)
    {
        Action<object> action;

        // Check if we have a matching strategy.
        if (!_Strategies.TryGetValue(value.GetType(), out action))
        {
            // If not, log error, throw exception, whatever.
            action = LogUnknownType;
        }

        // Perform the matching strategy on the given value.
        action(value);
    }

    private static void ComputeDateTime(object source)
    {
        // We get an object, but we are sure that it will always be an DateTime.
        var value = (DateTime)source;
        Console.WriteLine("We've got an date time: " + value);
    }

    private static void ComputeInteger(object source)
    {
        // We get an object, but we are sure that it will always be an int.
        var value = (int)source;
        Console.WriteLine("We've got an integer: " + value);
    }

    private static void LogUnknownType(object source)
    {
        // What should we do with the drunken sailor?
        var unknownType = source.GetType();
        Console.WriteLine("Don't know how to handle " + unknownType.FullName);
    }
}
Oliver
  • 43,366
  • 8
  • 94
  • 151
1

Here's an example of exactly why you would do this:

class MyClass
{
   public int Id {get;set;}
   public string Name {get;set;}
   public decimal Val {get;set;}
}
int i = 0;
var myClassImp = new MyClass();
foreach (var val in new [object]{"10", "My name", "100.21"} // Could be read from some data source, such as an excel spreadsheet
{
   var prop = typeof(MyClass).GetProperties().ElementAt(i++);
   // !!!!!! THROWS EXCEPTION  !!!!!!!
   prop.SetValue(myClassImp, System.Convert.ChangeType(val, prop.PropertyType), null);
}

The reason for this is because the value is a boxed object... at runtime you do not know the type, so you would have to unbox to the prop.PropertyType

1

A pragmatic solution; try and use a TypeConverter directly, and if that fails, convert to string and back again:-

private static T GetValueOfType<T>(this ManagementBaseObject MBO, String FieldName) {
    T lResult;

    try {
        Object lObj = MBO[FieldName];

        var lSrcType = lObj.GetType();
        var lDestType = typeof(T);

        if (lDestType.IsValueType && lDestType.IsAssignableFrom(lSrcType)) {
            lResult = (T)lObj;
            return lResult;
        }

        var lDestTC = TypeDescriptor.GetConverter(typeof(T));
        if (lDestTC.CanConvertFrom(lSrcType)) {
            lResult = (T)lDestTC.ConvertFrom(lObj);
        } else {
            var lSrcTC = TypeDescriptor.GetConverter(lSrcType);
            String lTmp = lSrcTC.ConvertToInvariantString(lObj);
            lResult = (T)lDestTC.ConvertFromInvariantString(lTmp);
        }
    } catch {
        lResult = default(T);
    }
    return lResult;
}
Richard Petheram
  • 805
  • 12
  • 16
1

Use expressions:

var y = DynamicCast(obj, desiredType);

static object DynamicCast(object source, Type type)
{
    var parameter = Expression.Parameter(typeof(object), "input");

    var cast = Expression.TypeAs(Expression.Convert(parameter, type), typeof(object));

    var lambda = Expression.Lambda<Func<object, object>>(cast, parameter);

    var func = lambda.Compile();

    return func(source);
}
Rob Lans
  • 21
  • 3
  • Does not work. The line `var lambda = Expression.Lambda>(cast, parameter)` errors with `Expression of type 'System.Nullable1[System.Int32]' cannot be used for return type 'System.Object'` – Jared Beach Dec 08 '16 at 16:22
  • Or to be more accurate. Doesn't work for non-nullable types such as int – Jared Beach Dec 08 '16 at 16:24
  • Fixed by adding an extra conversion – Rob Lans Dec 09 '16 at 11:03
  • It still does not work. What is returned by the lambda is still an object wrapping an int. You can see in the debugger. http://i.imgur.com/d0Ba0Al.png – Jared Beach Dec 09 '16 at 13:58
0
public static string GetType(object data)
{
    Type type = data.GetType();
    return Convert.ChangeType(data, type).GetType().Name;
}

Hi,this method receives object data and returns string type name of object. Hope this is what you need.

-1

you can try using the dynamic runtime

    [Test]
    public void Test_UnboxingAtRuntime()
    {
        object boxed = "Hello";

        //this line is commented out as it does not compile
        // OverloadedMethod(boxed);

        var result = CallCorrectMethod(boxed);
        Assert.That(result, Is.EqualTo("string"));

        boxed = 1;
        result = CallCorrectMethod(boxed);
        Assert.That(result, Is.EqualTo("int"));
    }

    public string CallCorrectMethod(dynamic t)
    {
        return OverloadedMethod(t);
    }

    public string OverloadedMethod(string s)
    {
        return "string";
    }

    public string OverloadedMethod(int s)
    {
        return "int";
    }
Rhys Bevilaqua
  • 2,102
  • 17
  • 20
  • 2
    There is no boxing in your sample to start with... And also no traces of unboxing either - not sure what exactly you suggest here... – Alexei Levenkov Mar 22 '13 at 08:03
  • What are YOU talking about? The cast of `string` to `object` is the textbook definition of boxing ([see this](http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx)), and once in the generic method it has been unboxed as T is equal to System.String, if this had just called `GetTypeName(str)` it would output "System.Object" – Rhys Bevilaqua Mar 27 '13 at 00:17
  • Sorry, my previous comment is now a little wrong as I changed the sample to make it clearer so there is no longer a generic method. – Rhys Bevilaqua Mar 27 '13 at 00:24
  • 2
    There is no boxing for `string` as it is reference type (unlike `int` which is value type as shown in linked article). I'm not exactly sure where did you find mentioning of string in relation to boxing in the article ... – Alexei Levenkov Mar 27 '13 at 00:29