我发现了类似的问题,但我仍然遇到麻烦:
- 希望能更好地描述问题吗?----
当我调用Web服务时,带回的响应是xml文档。该文档定义了要返回的类,然后通过将xml反序列化为8种不同类型中的1种来设置所有值。
现在当我receipt.Item
时,我得到了返回的类型;但由于使用Web服务调用设置接口的方式,我无法访问任何项成员变量,除非我键入cast receipt.Item
。这是通过开关盒完成的。但我希望在switch case之外创建对象并在switch case中初始化它,以便稍后我可以在代码中访问它。这就是为什么我不在switch case中创建那种类型的新对象并在那里工作(或调用函数)。
我从我正在调用的Web服务中获得了一个总体返回类型的响应,并且Web服务可以有8种不同的结果类型。我需要创建一个可以返回的8种返回类型中的1种的实例。
所以这是一个更直观的结构
Response
accountUpdaterRespType
endOfDayRespType
flexCacheRespType
响应对象的代码:
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;
}
}
}
当我得到Response的返回对象时,我可以通过responseObject.Item
获取类型并对其执行GetType()
。所以我可以尝试输入一个新对象。
我必须这样做,因为当我responseObject.Item
时,我无法访问不同对象类型中的不同变量。所以我试图在这样的开关盒中输入一个新对象:
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;
}
答案 0 :(得分:5)
我会在回答之前重试你的问题。
您正在尝试创建对现有实例的类型化引用。您已经拥有一个对象实例,该实例保存在object
类型的变量中,但是为了能够访问成员,需要将其强制转换。
通过在代码中获取变量类型,您仍然无法在开发时访问对象成员。
使用字符串检查对象类型不是一个好主意。您的问题的工作解决方案将遵循
// 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
}
你明白了。请注意,这不是编写代码的好方法。上面的示例只是为了让您启动并运行。你应该熟悉面向对象的编程概念,特别是这种情况,多态性。
另一种(基本相同)处理方法是:
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");
你是对的,多态性是一种解决方案,在这种情况下,对象具有相似的特征,需要以类似的方式处理。上面的两个解决方案是您无需了解C#语言的最佳方法。第二种解决方案可以更好地分离责任。
您可以使用reflection获得更通用的解决方案。使用System.Reflection
中的方法,您可以对处理程序方法进行更通用的解析。举例来说:
您有Response
对象,如您所描述的。您还有一个可以处理不同类型对象的类。例如:
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) { /* */ }
...
}
收到回复后,您将能够确定动态调用哪个处理程序,而无需手动添加每个类型,如下所示:
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 });
这是在SO编辑器中写的,因此可能无法立即运行:)
答案 1 :(得分:1)
要扩展NikolaRadosavljević的第一个答案,您可以创建一个这样的扩展方法:
public static IfType<T>(this object o, Action<T> action) {
var asType = o as T;
if (asType != null) {action(asType);}
}
然后,您可以执行以下操作,为您提供对每种特定类型的所有成员的设计时访问权限:
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
});
仍有很多噪音,但更简洁一点。
<小时/> 要以OOP方式执行此操作,请定义一个接口:
interface IResponseItem {
void DoAction();
}
然后,每个项目类型都应实现IResponseItem
接口:
public class AccountUpdater : IResponseItem {
private int data;
public void DoAction() {
Console.WriteLine(data);
}
}
然后,您将Response.Item
的类型定义为IResponseItem
,您可以直接调用DoAction
,而无需知道项目的实际(又称具体)类型:
Response.Item.DoAction();
这是多态 - 具有实现相同成员的多个继承/实现类型(IResponseItem
等)的公共基类型(AccountUpdater
)({{1做不同的事情。 (Polymorphism in the C# Programming Guide on MSDN)
答案 2 :(得分:0)
您的问题可以通过实现interface或抽象基类来解决。
如果类型实现其他类型,则可以存储在较低类型的变量中。例如,在.Net中,每个类型都派生自基本类object
。这允许您在对象变量中存储List
,String
和其他所有类型。
类似地,您可以使用接口来存储实现该接口的实例。接口基本上是一个类需要实现的方法和属性的列表。
在你的情况下,我建议你应该添加更高级别的抽象。例如,您可以创建一个接口
interface IResponse {
int StatusCode {get;}
string Content {get;}
...
}
您可以在每个响应中实现此接口。
public class EndOfDayResponse : IResponse
{ ... }
Receipt.Item
的类型将是IResponse而不是object。然后,您可以使用response is EndOfDayResponse
检查实际类型,然后进行适当的转换。