Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Saturday, November 8, 2008 12:29 AM
How do I return a down-cast object to its original type, if the original type is not set at compile-time?
I have a number of different objects that I am storing in a treeview control. When the user selects a treeview node, the object stored in the node's Tag property is returned. But it is returned as type object. I need to get the object back to it's original type.
Here's my problem: I won't know the original type of the object until it is returned. The object in the node could be any one of a dozen or so types.
I know that I can find the original type of the object using its GetType() method. But how do I cast the object back to that original type, so that I can access its members?
For example, I store a Widget in a treeview node's Tag property. When the user selects the node, the widget is returned as a type object. I can use the object.GetType() method to discover that the object is, in fact, a widget. But then how do I up-cast back to the Widget type that I need?
If I knew in advance that I would need a Widget object, then it's easy:
Widget myWidget = (Widget)Node.Tag
But I won't know that a Widget is what I need until I get the object out of the Tag property and invoke the object.GetType() method on it. So, what I think I need is the line of code that gets me the Widget object reference that I can cast my widget to.
Thanks in advance for your help!
David Veeneman
Foresight Financial
All replies (5)
Saturday, November 8, 2008 1:14 AM ✅Answered
Well, the instance itself (that data in memory that represents the object) is always typed as the specific concrete subclass. It's only the references to that instance that change type. So even though the object in the Tag property is always of type Widget (for example), the Tag property accessor returns a reference that is of type Object (that is in turn pointing to the Widget instance). The Object reference only knows about Object members, but it is in fact pointing to a Widget instance that includes all the Widget class-defined members.
There are a couple of ways of casting the reference to the actual instance type. You already hinted at one - directly using a cast. You'll have to do that in a switch or "if..if else" structure in order to identify the actual type. For example:
If (Node.Tag is Widget)
Widget myWidget = (Widget)Node.Tag;
myWidget.DoSomething();
Else If (Node.Tag is Sprocket)
Sproket mySprocket = (Sprocket)Node.Tag;
mySproket.DoSomethingElse();
Else If (Node.Tag is Gizmo)
Gizmo myGizmo = ... // you get the picture
But checking to see if an instance is a certain other type and then casting it is fairly costly, so a more performant way of doing that is to use the As operator.
Widget myWidget = Node.Tag As Widget;
if (myWidget == null)
// Node.Tag was null or not a Widget
In any case, you will have to know the total sum of the types being used. (Otherwise, how you know which members to use in code?)
However, if all the instances share a common base class or interface, then you can simply cast to that interface and directly call a member they all share. This is really the more elegant way of doing it (if it's possible) and doesn't require a lengthy If..Else If branching structure. For example, if Widget, Sprocket, and Gizmo all inherited from Component, you can simply cast to Component:
Component c = Node.Tag As Component;
MessageBox.Show(c.DesignMode.ToString());
Since all the three types in use inherit from Component, they are all guaranteed to have the member DesignMode, and therefore, you can cast a reference to that level and invoke the member across all the instances, regardless of their physical type.
-Rob Teixeira
Saturday, November 8, 2008 2:25 AM ✅Answered
Unfortunately, what you've just asked for is a logical impossibility. At some point in your code, you have to declare a variable as the original type, in order to reference the object in a strongly-typed environment. There's nothing in C# that will allow you to dynamically create a strongly-typed object "on the fly" without using either some sort of case statement, or creating some kind of factory interface to handle the routing of these objects.
One solution you may find that works well for you is to create some kind of generic interface that handles each data type you want to work with. Once you do that, create implementations of this generic method. After creating the implementation, you'll want to write a method that will hunt down your impementation, based on the generic type, using reflection, and invoke a method to reintroduce it to the strongly typed model. Something like this might work. Cut and copy this code, and examine it. The downside is that you'll need to have an implementation for every type you wish to add to the tag, but this is the only way to reintroduce a down-cast object to the world of strongly typed objects.
| public interface IProcessor<T> |
| { |
| T Process(T obj); |
| } |
| public class Car |
| { |
| } |
| public class Person |
| { |
| } |
| public class CarProcessor : IProcessor<Car> |
| { |
| public Car Process(Car obj) |
| { |
| MessageBox.Show("Car"); |
| return obj; |
| } |
| } |
| public class PersonProcessor : IProcessor<Person> |
| { |
| public Person Process(Person obj) |
| { |
| MessageBox.Show("Person"); |
| return obj; |
| } |
| } |
| static class Program |
| { |
| /// <summary> |
| /// The main entry point for the application. |
| /// </summary> |
| [STAThread] |
| static void Main() |
| { |
| object car = new Car(); |
| ProcessValue(car); |
| object person = new Person(); |
| ProcessValue(person); |
| } |
| static void ProcessValue(object value) |
| { |
| Type genericType = value.GetType(); |
| Type madeType = typeof(IProcessor<>).MakeGenericType(genericType); |
| Type factoryType = Assembly.GetExecutingAssembly().GetTypes().Where(t => madeType.IsAssignableFrom(t)).FirstOrDefault(); |
| object factory = Activator.CreateInstance(factoryType); |
| factoryType.GetMethod("Process").Invoke(factory, new object[] { value }); |
| } |
| } |
David Morton - http://blog.davemorton.net/
Saturday, November 8, 2008 1:50 AM
Thanks, Rob-- I just discovered I don't need to upcast to do what I wanted to do.
I am modeling an NHibernate mapping document, using a different class for each different type of element in the document. Why use a class to stand in for an element, instead of simply manipulating an XmlElement object? Using a class lets me build logic into the element handling, and it also facilitates using enums for list choices, and so on.
Anyway, my treeview represents the elements of a mapping document in a hierarchical structure. When a user clicks on a node, the object that represents the element is passed to a WinForms PropertyGrid control, where the attributes of the element can be edited. Since there are a number of different elements in a document, and since a document can be composed of any number of different elements in different combinations, I was trying to avoid lengthy switch-case statements. My goal is to be indifferent to the actual type of the object returned from the node.Tag() property. Whatever I get from the treeview, I pass to the property grid. Very simple.
I had assumed that since the return value from the treeview was cast to type object, the PropertyGrid would not be able to access the object's properties. I was pleasantly surprised to find out I was wrong. Even though I pass the return value to the PropertyGrid as an object type, all of its native properties appear in the Properties grid. So, I have achieved type indifference and gotten the job done with a minimum of code.
But I'm still wondering if there is a way to return a downcast object to its original type, if you don't know in advance what that type was. I tried playing with an implicitly typed variable declaration (var), but I couldn't get that to work. If anyone knows how to do it (without switch-casing through all the possibilities), I'd still like to know. Thanks.
Sunday, November 9, 2008 11:52 PM
That's what I finally concluded. I had derived all my domain classes from a domain base, so I hand them back and forth typed as the base class.
David Veeneman Foresight Financial
Saturday, May 26, 2012 1:34 PM | 1 vote
Hi my sister edited a picture in paint and she painted a bunch of crazy things on it. I would like to get back the original picture. Please tell me how :)