cast with a Type variable
Asked Answered
O

4

5

The below code won't work, I wanted to know how I can dynamically cast an instance to a type determined at runtime?

Convert.ChangeType() returns an Object that still needs to be cast. So does all attempts to Invoke() a GetConstructor() or Activator.CreateInstance(), see below. At some point I need to explicitly cast in code, I'm hoping to avoid it or push it out as far as possible.

Type type = Type.GetType ("RandomChildClass");
Object obj = Activator.CreateInstance (type, new Object[]{ "argument" });
var instance = (type)obj;

I know I can create a method to accept < T >, but I'd still have the same problem of not knowing how to call it with a dynamic random type Casting a variable using a Type variable

Ovarian answered 25/1, 2014 at 2:24 Comment(2)
What are you trying to do with instance after you cast?Osteal
just adding it to a List of a base typeOvarian
F
6

It is not possible to use a Type value to determine the type of an expression. (Generics type parameters are different than values as they are codified into the type-system.)

The value of the variable is from the run-time code execution, while the expression type is a compile-time construct. Needless to say, the compilation occurs before the code ever runs so using a variable for a cast is impossible.

Reflection (albiet unwieldy) or dynamic (which is basically easier-to-use-reflection) allow invoking arbitrary methods or accessing properties/fields against a generic object-typed expression - this is occasionally refered to as "late binding". However, the type of the expression upon which operations is invoked is still object.

Interfaces can be used to unify disparate class implementations for proper static typing. The newly created object can then cast to the applicable interface(s) are required. Just like with other expressions, the type is a compile-time construct (and as such the interface must be directly specified), but the code is now free from a particular class.

If creating a system such that these "dynamic classes" are to be used directly in statically typed (C#) code, and the interfaces can be guaranteed or are constrained to a small set, then using interfaces is likely the cleanest approach: e.g. var myAction = (IMyAction)obj. Otherwise, fall back to dynamic access - direct or behind a facade.

Freed answered 25/1, 2014 at 3:12 Comment(3)
my hope was to dynamically fill in the "(IMYACTION)" part of the cast, but from your explanation it doesn't appear at all possible. Is that correct?Ovarian
@Ovarian That's correct. It's not possible, for the same reason.Freed
@Ovarian You may find the DuckTyping library (as discussed here) interesting.Freed
A
4

Define your variable as dynamic and it should work, I mean still you can't cast it but you can access your methods:

dynamic obj = Activator.CreateInstance (type, new Object[]{ "argument" });

For example:

enter image description here

Antiar answered 25/1, 2014 at 2:34 Comment(1)
is it possible without using dynamic? I'm getting compile errorsOvarian
O
4

Based on your comment you are trying to add it to a List<SomeDynamicClass>. My first thought is that if you can't statically access SomeDynamicClass then I'm curious how, or why, you have a List<SomeDynamicClass>. It sounds like the code would be much better off with List<object> or List<dynamic> instead.

If you are constrained to List<SomeDynamicClass> then it sounds like you should just jump directly to using reflection to call the Add method

var listType = theList.GetType();
var method = listType.GetMethod("Add");
method.Invoke(theList, obj);
Osteal answered 25/1, 2014 at 3:17 Comment(2)
I'm sorry for the confusion, I'm really just wanting of a way to dynamically cast to an particular child class. After that has occurred a simple Add on a list of the base would work.Ovarian
If I construct a child object (via invoking the constructor) and cast it to the parent, later when I want to use it, I'd still need to explicitly cast it to the child. But if I am able to keep its type, add it to a list of the base class, at use it still retains its own type (not the base), am I wrong in this assumption?Ovarian
I
1

If you statically know the type of the base class, cast to that type. For example:

void AddToList(List<Control> list, string typeName) {
    var control = (Control)Activator.CreateInstance(typeName);
    list.Add(control);
}

Then

var list = new List<Control>();
AddToList(list, "Button"):
AddToList(list, "Label"):
AddToList(list, "ListBox"):

If you need to do something type-specific later, you can test the type and cast:

foreach (Control child in list)
    DoSomethingWithThe(control);

void DoSomethingWithThe(Control control)
{
    Button button = control as Button;
    if (button != null)
    {
        button.Click += Button_Click;
        return;
    }

    Label label = control as Label;
    if (label != null)
    {
        label.MouseOver += Label_MouseOver;
        return;
    }
    //... etc.
}

Or use the OfType method:

foreach (Button button in list.OfType<Button>())
    button.Click += Button_Click;

foreach (Label label in list.OfType<Label>())
    label.MouseOver += Label_MouseOver;

You can also use reflection, but that's fairly cumbersome so I won't give an example. Reflection would be useful if you have a property with the same name that's defined on different subtypes of the base type, but not on the base type. If there are many subtypes, the type checking would get cumbersome, so reflection might be a better approach.

Impending answered 25/1, 2014 at 6:3 Comment(2)
thanks, I'd like to try to keep the type of the child. Is it possible?Ovarian
@Ovarian As user2864740 answered, no, it is not possible to retain the static type of the child at compile time. However, the child object retains its run-time, always. For example, this code results in a list containing one string and one boxed int: var objects = new List<object> { "One", 2 }. These objects will always be a string and a boxed int. If you need to do something type-specific to the children after retrieving them from the list, test the type and cast (possibly with the Linq OfType method), or else use reflection. I'll add a couple of examples.Impending

© 2022 - 2024 — McMap. All rights reserved.