2

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;
}
Community
  • 1
  • 1
B-M
  • 1,231
  • 1
  • 19
  • 41
  • It is hard to understand your question. Are you trying to create a new instance of the class? If yes why you are not creating it in your switch statement? – Dilshod Sep 09 '13 at 22:00
  • Also, are the different response type somehow similar so that you can create an interface which every response type implements? In that case you could try to implement your method as generic method and use the default keyword. – ChrisK Sep 09 '13 at 22:04
  • 1
    They are similar in a sense that some (maybe 3) of the return types values are the same but the vast majority of them (over 15) are not. Also @Dilshod I am trying to come up with a better explanation. – B-M Sep 09 '13 at 22:06
  • 1
    So the response types do not implement the same variables or functions? There isn't a variable (let's say StatusCode) that is present in every response type? Also, do you need the response type to execute functions on it? Or are you interested in only the type? In that case you could replace it with an Enum. – ChrisK Sep 09 '13 at 22:11
  • How do you want to use the result of your class generation? – Dmitrii Dovgopolyi Sep 09 '13 at 22:11
  • @ChrisK there is a process status code present in each yes. I will need the type to execute functions past the switch case (I could execute all the code within the switch case, I'd just rather not). – B-M Sep 09 '13 at 22:14

3 Answers3

5

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 :)

Nikola Radosavljević
  • 6,871
  • 32
  • 44
  • Polymorphism, from my understanding and not really working with it much, is that it is used for objects are very similar isn't it? These objects have roughly 3 common values then the rest are different based on the response type. That doesn't seem like a good fit in this situation. Your solution though, could be potentially the way I go. – B-M Sep 09 '13 at 22:21
  • @Brett with this bad design, when one method handle all the different answers from the server, it the only possible solution. – Dmitrii Dovgopolyi Sep 09 '13 at 22:33
  • @Brett See my answer for an idea of how polymorphism would be useful here. – Zev Spitz Sep 09 '13 at 22:53
  • @Brett, you set constraint that all those different types are expected to be handled by one method. See more elaborated answer. – Nikola Radosavljević Sep 09 '13 at 22:55
1

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)

Zev Spitz
  • 13,950
  • 6
  • 64
  • 136
  • 1
    This is a better general approach. However, `Reponse.Item` seems to be a data model class which is not always a good place to place your business logic. – Nikola Radosavljević Sep 09 '13 at 22:57
0

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.

ChrisK
  • 1,216
  • 8
  • 22
  • What's the difference if the variable in question is of type `IResponse` or of type `object`? Specific type checking is still necessary. What added value is provided by the interface? Now if the interface had a method with the action to perform with the response item, that would be a different story. – Zev Spitz Sep 09 '13 at 22:39
  • It really depends on the rest of the code. For one, I personally think that an additional interface makes the code more readable. Also, if he is using stuff like the StatusCode somewhere else he doesn't have to use type checking/casting. – ChrisK Sep 09 '13 at 22:55
  • As to your second point, he wants to access members at design-time -- **because when I do responseObject.Item I don't have access to the different variables that are in the different object types** -- so he will have to do type checking and casting to a new variable. – Zev Spitz Sep 09 '13 at 23:04