0

I want to cast an object to a type that a System.Type object defines. I only know the object and the Type as inputs, I can't add type parameters.

void Test()
{
    object obj = 3;
    Type type = typeof(int);

    int number = Cast(obj, type);

    Console.WriteLine($"{number}, {number.GetType()}"); // Should output "3, System.Int32"
}

// I can't change the inputs
??? Cast(object obj, Type type)
{
    // return the obj casted to type, but how?
}

I guess there's a way to solve it using reflection but I couldn't find anything like this.

  • Do you mean something like this? [How do I use reflection to call a generic method?](https://stackoverflow.com/a/232621/4331925) – Paweł Własiuk May 31 '20 at 00:13
  • No, you can't do that. The object is of the correct type, but that type is only known at run time, and things like casting are mostly compile-time behavior. If you know the type at compile time you can do the cast. Or, if you know an interface that your type implements, you can cast to that interface. However, if you know none of this at compile time, your best bet is to use Reflection's `Invoke` method (as described in @pawelwlasiuk's link) – Flydog57 May 31 '20 at 00:23
  • 3
    @Flydog57 well no, OP can use [`Convert.ChangeType(Object, Type)`](https://learn.microsoft.com/en-us/dotnet/api/system.convert.changetype?view=netcore-3.1#System_Convert_ChangeType_System_Object_System_Type_) which coincidentally matches the signature of his own `Cast` method –  May 31 '20 at 00:45
  • @MickyD You're right, in this example `obj` could be passed directly to `Console.WriteLine`. Casting wasn't even necessary. – Paweł Własiuk May 31 '20 at 00:53
  • @mickyd: That changes the type. However, in this case, or in the more common case of using `Activator.CreateObject` the object is already the right type. What you can't do is magically make a variable that is an instance or s reference to an instance of the type of interest. Both `Convert.ChangeType` and `Activator.CreateObject` return a reference to an `object`. His code (changed as you suggest) would be `int number = Convert.ChangeType(obj, type);` That won't compile (complaining that the object returned by the `ChangeType` call can't be converted to an int) – Flydog57 May 31 '20 at 01:33
  • You should've shown what you actually intend on doing with the value once it's been through your Cast function. What you do with it is more important I think, because as it stands this is almost certainly an XY problem. If you showed us why/what you intend to do you with the "new" value you could potentially get a better answer (to your *unasked question*, that is) that doesn't rely on `dynamic` which is incredibly overkill if you intend on using it for a few primitive values (and has huge costs, both DLR-related and the loss of static-typing and compile-time safety...tho such is reflection) – pinkfloydx33 May 31 '20 at 09:14

2 Answers2

2

It is possible to do it without changing the Cast method into a template. It needs to return dynamic type. To cast, you should use the method Convert.ChangeType.

//I can't change the inputs
dynamic Cast(object obj, Type type)
{
    // return the obj casted to type, but how?
    return Convert.ChangeType(obj, type);
}
  • That is new to me. Surely that would fail most of the time? How does that work? – Rno May 31 '20 at 00:39
  • 2
    The word `dynamic` means that the type is not checked during compilation, so thanks to that it was possible to assign the result of this method to `int number`. It's much slower than using other types and also less safe - the compiler should be your friend. As to failing most of the time? I'm not sure, I checked the method for 'double' type and it was working, but I didn't do more tests. – Paweł Własiuk May 31 '20 at 00:49
  • Thanks, it worked. I have seen Convert.ChangeType during my research, but I though I need to implement `System.IConvertable` (which is against my will). And according to IntelliSense, it returns an object, so I though it convert the object in an other way, and I need to cast it later. Can you explain me why does it work? Cheers @PawełWłasiuk – Janos Nagy May 31 '20 at 08:46
  • ChangeType does in fact require that value be IConvertible otherwise it will throw. So it's still "against your will" but fortunately `int` is IConvertible. Using dynamic has a lot of overhead (it has to spin up the DLR upon usage) and it still technically types the variables/return type as `object` (with a special attribute). I'd strongly discourage dynamic here.. Especially if just using for primitive types.. and perhaps that you may have to reconsider some of your design – pinkfloydx33 May 31 '20 at 09:00
  • I tried it with a custom class that only derives from `object` and does not implement `IConvertable` and it worked. About my application design I'm not sure how else I can do it. – Janos Nagy May 31 '20 at 09:16
  • @JanosNagy the only way it works if IConvertible is not implemented is if the type of the value is already the target type, in which case the value is just returned back (as `object`) https://github.com/microsoft/referencesource/blob/a7bd3242bd7732dec4aebb21fbc0f6de61c2545e/mscorlib/system/convert.cs#L339 If that's all you need, use ConvertType directly and omit dynamic. Obviously that limits you to typing variables as object, but that's much better than dynamic which could throw at runtime and has tons of overhead. If youre using custom types your question should've shown that instead of int – pinkfloydx33 May 31 '20 at 10:17
1

You can use ChangeType method :

1 - Cast method:

public static T Cast<T>(object obj)
{
    return (T)Convert.ChangeType(obj, typeof(T));
}

2 - Test method:

void Test()
{
    object obj = 3;

    int number = Cast<int>(obj);

    Console.WriteLine($"{number}, {number.GetType()}");
}

Result:

3, System.Int32

I hope you find this helpful.

Mohammed Sajid
  • 4,778
  • 2
  • 15
  • 20