tags:

views:

251

answers:

3

Hi,

I have a System.Type stored in a variable. I wish to Change an object's type to this type.

I have the following code, but cannot get the Type to change to the type.

Ideally I'd like: var intTest3 =(MyType)Convert.ChangeType(test, MyType); to return an int, when : MyType is a System.Int32

Here's my working so far - where am I going wrong?

        // object to cast to int
        object test = 1;

        // INT32 type
        Type MyType = typeof(System.Int32);

        // explicit type int WORKS
        var intTest = (int)Convert.ChangeType(test, typeof(Int32));


        // explicit type to int WORKS
        var intTest2 = (int)Convert.ChangeType(test, MyType);

        // explicit type to int WORKS - but as object
        object intTest3 = Convert.ChangeType(test, MyType);


        // cast to my type DOESNT WORK
        var intTest3 =(MyType)Convert.ChangeType(test, MyType);

Thank you!

A: 

MyType is a Type, which is a variable containing the definition information of a struct / class. Casting by putting the type in brackets e.g. (int)value; requires the item in the brackets to be an actual class or struct, not a variable.

ck
+3  A: 

This isn't really about Convert.ChangeType - it's about casting. The type you're casting to has to be known at compile time, although it could be a generic type. In this case, as far as the compiler is concerned MyType could refer to any type. It doesn't "know" that it will definitely have the value typeof(int), so it won't just emit a cast to int.

In this case, what would you expect the compile-time type of intTest3 to be?

What's the bigger picture here? What are you really trying to do? How would you want to use the value of intTest3?

Jon Skeet
This isn't the best explanation of the problem, especially as the type is hard-coded.
ck
I am translating data between 2 APIs. One API provides an object containing data, and a variable containg the System.Type of this data. These are set at compile time.The other API requires that any input is if of the "correct type" - so I cannot send in just the object, but rather, if the object's type is "Int32", then it expects an Int32 variable.So I guess the question is "How to cast an object to a type, that is known only at run time".
bob
@bob: What would you expect such a cast to do? How would you expect to use the result? Pretend that "var" doesn't exist, as it's not *really* relevant here - how would you have declared `intTest3` otherwise? Can you use a generic method instead? (That would let you cast to `T` having passed in `typeof(T)` to `ConvertType` if necessary.) If you could post fuller code for how you want to use whatever you come up with, that would be helpful.
Jon Skeet
currently, I have a switch statement on MyType.case Int32: Convert.ToInt32(test); break;case Int64: Convert.ToInt64(test); break;But was hoping to remove the ugly switch...
bob
@bob: But what are you doing with the result of Convert.ToInt64 afterwards? The context is very important here.
Jon Skeet
It's being passed into an Api method. The Api method is overloaded, so it can accept an input variable of any type, except for type Object.
bob
@bob: Are, so you want to change which overload you call based on the value of a variable? Unless you're using C# 4 (in which case you could use `dynamic`) you're basically out of luck for clean solutions. Either stick with the switch or you'll need to find the right method with reflection.
Jon Skeet
Jon - Thank you!
bob
+2  A: 

It's not supposed to. MyType isn't a "type" as far as C# is concerned, it's a variable of type "Type". The "type" of "MyType" is "Type", but you can't cast 1 to "Type".

In this situation, you've entered "reflection-land" but you're really trying to find a way out. I'm sorry, but there's no way to get back to strongly-typed-land from this sort of situation.

One workaround you could try to do however, is to move that last line into another generic method, and then invoke the method generically:

public static void LeaveReflectionLand<T>(object value)
{
     T newItem = (T)value;
}

Then from outside, you'd have to do something like this:

this.GetType().GetMethod("LeaveReflectionLand", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(MyType).Invoke(null, test);

But of course, that's a huge and scary workaround. Check Jon Skeet's answer... I think it put things clearly.

The long and the short of it is, as MyType is an instance of the class Type, it could represent any type at all. It's a variable just like int, as far as the compiler is concerned. It's not a proper "type" in and of itself, it's a variable that "describes" a type. Because it's a variable that describes a type, there's no way, at compile time, for the compiler to know the actual type described by MyType, so this sort of thing isn't allowed.

It's not unlike doing something like this:

int test = 1;
int MyType = 2;
int anotherTest = (MyType)test;

Obviously, you can't do this. MyType is a variable of type Int32. The same is true when the type of MyType is Type, only in that situation MyType is a variable of type Type.

The key to understanding this is the difference between the "Type class" and the "compiler type".

I hope this helps. It's not the easiest thing to explain in words.

David Morton
perfectly clear - thank you!
bob