Reputation: 1288
Similar questions I have found, but I am still having troubles:
-- 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
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.
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
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
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