B-M
B-M

Reputation: 1288

Create new object from instance of another

Similar questions I have found, but I am still having troubles:

  1. Dynamically create an object of <Type>
  2. Get a new object instance from a Type

-- A better description of the problem, hopefully?----

When I call the web service, the response that is brought back is an xml document. That document defines the class that is being returned and then all the values are set through deserializing the xml into 1 of the 8 different types.

Now when I do receipt.Item I get the type that is returned; but because of the way the interface is set up with the web service call I can't access any of the items member variables unless I type cast receipt.Item. That is being done with the switch case. But I want the create the object outside of the switch case and initialize it inside the switch case so I can access it later in the code. That is why I do not create a new object of that type in the switch case and do my work there (or call a function).


I have an overarching return type of Response from a web service that I am calling and the web service can have 8 different result types. I need to create a instance of the 1 of the 8 return types that can be returned.

So here is the structure for a more visual purpose

Response
     accountUpdaterRespType
     endOfDayRespType
     flexCacheRespType

The code for the response objects:

public partial class Response {

    private object itemField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("AccountUpdaterResp", typeof(accountUpdaterRespType))]
    [System.Xml.Serialization.XmlElementAttribute("EndOfDayResp", typeof(endOfDayRespType))]
    [System.Xml.Serialization.XmlElementAttribute("FlexCacheResp", typeof(flexCacheRespType))]
    public object Item {
        get {
            return this.itemField;
        }
        set {
            this.itemField = value;
        }
    }
}

When I get the return object of Response I can get the type by doing responseObject.Item and do a GetType() on that. So that is what I have available to me to attempt to type cast a new object.

I have to do this because when I do responseObject.Item I don't have access to the different variables that are in the different object types. So I am trying to type cast a new object in a switch case like so:

object newReceipt = Receipt.GetType(); //this is where I would get the type I assume?? I don't know

string type = Receipt.Item.GetType().ToString();

switch (type)
{
    case "accountUpdaterRespType":
        newReceipt = (accountUpdaterRespType)Receipt.Item;

        break;
    case "endOfDayRespType":
        newReceipt = (endOfDayRespType)Receipt.Item;
        break;
    case "flexCacheRespType":
        newReceipt = (flexCacheRespType)Receipt.Item;
        break;
}

Upvotes: 2

Views: 4759

Answers (3)

Zev Spitz
Zev Spitz

Reputation: 15375

To expand on Nikola Radosavljević's first answer, you could create an extension method like this:

public static IfType<T>(this object o, Action<T> action) {
    var asType = o as T;
    if (asType != null) {action(asType);}
}

Then you could do the following, giving you design-time access to all the members of each specific type:

Receipt.Item.IfType<accountUpdater>(r => {
    Console.WriteLine(r.SomeAccountUpdaterProperty);
});
Receipt.Item.IfType<endOfDayResp>(r => {
    //Do something with endOfDayResp here
});
Receipt.Item.IfType<flexCacheResp>(r => {
    //Do something with flexCacheResp here
});

Still a lot of noise, but a bit more concise.


To do this the OOP way, define an interface:

interface IResponseItem {
    void DoAction();
}

Then, each item type should implement the IResponseItem interface:

public class AccountUpdater : IResponseItem {
    private int data;
    public void DoAction() {
        Console.WriteLine(data);
    }
}

Then, you define the type of Response.Item as IResponseItem, and you can call the DoAction directly, without knowing the actual (a.k.a. concrete) type of the item:

Response.Item.DoAction();

This is polymorphism - having a common base type (IResponseItem) with multiple inheriting/implementing types (AccountUpdater etc.) that implement the same member (DoAction) to do different things. (Polymorphism in the C# Programming Guide on MSDN)

Upvotes: 1

Nikola Radosavljević
Nikola Radosavljević

Reputation: 6911

I'll try to restate your question before answering it.

You're trying to create a typed reference to existing instance. You already have an instance of an object, held in a variable of type object, but want to cast it up in order to be able to access members.

By getting variable type in code, you still won't be able to access object members in development time.

Using strings to check object type is not a good idea. Working solution to your problem would be following

// as is a type of cast. if Receipt is of type cast,
// it will return an object and put it into accountUpdater
// variable. If Receipt is not of that type, it will place null
// into accountUpdater variable
var accountUpdater = Receipt.Item as accountUpdater;
if (accountUpdater != null)
{
    // Do something with account updater here. E.g.
    Console.WriteLine(accountUpdater.SomeAccountUpdaterProperty);
}
var endOfDayResp = Receipt.Item as endOfDayRespType;
if (endOfDayResp != null)
{
    // Do something with endOfDayResp here
}   
var flexCache = Receipt.Item as flexCacheRespType;
if (flexCache != null)
{
    // Do something with flex cache here
} 

You get the idea. Mind you, this is not a very nice way to write code. Example above is just to get you up and running. You should get acquainted with object-oriented programming concepts, and for this case particularly, polymorphism.

Another (essentially same) way to handle this would be:

var accountUpdater = Receipt.Item as accountUpdater;
if (Receipt.Item is accountUpdater)
    HandleAccountUpdater((accountUpdater)Receipt.Item);
else if (Receipt.Item is endOfDayRespType)
    HandleEndOfDay((endOfDayRespType)Receipt.Item);
else if (Receipt.Item is flexCacheRespType)
    HandleFlexCache((flexCacheRespType)Receipt.Item);
else
    throw new InvalidArgumentException("Unexpected parameter type");

You are correct, polymorphism is a solution in situations where objects have similar traits and need to be handled in similar fashion. Two solutions above are best way you can do without learning a little bit more about C# language. Second solution provides better separation of responsibilities.


You can get more generic solution using reflection. Using methods in System.Reflection you can make a more generic resolution of handler methods. Take following for example:

You have Response object such as you described. You also have a class which can handle different types of objects. For example:

public class ResponseHandler
{
    public void Handle(accountUpdater parameter) { /* */ }
    public void Handle(endOfDayRespType parameter) { /* */ }
    public void Handle(flexCacheRespType parameter) { /* */ }
    public void Handle(TypeD parameter) { /* */ }
    public void Handle(TypeE parameter) { /* */ }
    ...
}

Once you receive response, you will be able to determine which handler to call dynamically, without adding each and every type manually, like so:

var handler = new ResponseHandler();
var handlerClassType = typeof(ResponseHandler); // This is how you get Type object from a type. Unlike, `GetType` on objects
var paramType = Response.Item.GetType();
// Get me method which is named Handle and takes parameters in parameter array
// handlerMethod will be of type MethodInfo. This is basically a descriptor of a
// method. Not a pointer to a method or some such...
var handlerMethod = handlerClassType.GetMethod("Handle", new Type[] { paramType });
// Throw exception if we don't know how to handle it
if (handlerMethod == null)
    throw new Exception("Handler not found for received response type");
// Invoke the handler. We need to provide the method descriptor with object which
// should execute the method, and parameters that the method takes
handlerMethod.Invoke(handler, new object[] { Response.Item });

This is written here in SO editor, so it may not run right away :)

Upvotes: 5

ChrisK
ChrisK

Reputation: 1218

Your problem could be solved with the implementation of either an interface or an abstract base class.

If a type implements other types, you can store in a lower typed variable. For instance, in .Net every type is derived from the basic class object. This is allows you to store a List, a String and every other type in a object variable.

Similar, you can use interfaces to store instances that implement that interface. A interface is basically a list of methods and properties that a class needs to implement.

In your case I'd suggest you should add a higher level of abstraction. For instance you could create a interface

interface IResponse {
    int StatusCode {get;}
    string Content {get;}
    ...
}

You could implement this interface in every response.

public class EndOfDayResponse : IResponse
{ ... }

The type of Receipt.Item would then be IResponse instead of object. You could then check the actual type with response is EndOfDayResponse and then do the appropriate casting.

Upvotes: 0

Related Questions