2

我发现了类似的问题,但我仍然遇到麻烦:

  1. 动态创建 <Type> 的对象
  2. 从 Type 中获取新的对象实例

-- 希望能更好地描述问题?----

当我调用 Web 服务时,返回的响应是一个 xml 文档。该文档定义了要返回的类,然后通过将 xml 反序列化为 8 种不同类型中的 1 种来设置所有值。

现在,当我这样做时,receipt.Item我得到了返回的类型;但是由于使用 Web 服务调用设置接口的方式,除非我键入 cast ,否则我无法访问任何 items 成员变量receipt.Item。这是通过开关盒完成的。但我希望在开关盒之外创建对象并在开关盒内对其进行初始化,以便稍后在代码中访问它。这就是为什么我不在 switch case 中创建该类型的新对象并在那里工作(或调用函数)的原因。


我有一个来自我正在调用的 Web 服务的总体返回类型 Response,并且 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 的返回对象时,我可以通过 do 来获取类型responseObject.Item并对其执行 a 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;
}
4

3 回答 3

5

在回答之前,我会尝试重述您的问题。

您正在尝试创建对现有实例的类型化引用。您已经有一个对象的实例,保存在 type 的变量中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# 语言即可完成的最佳方法。第二种解决方案提供了更好的职责分离。


您可以使用反射获得更通用的解决方案。使用方法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 编辑器中编写的,因此它可能不会立即运行 :)

于 2013-09-09T22:12:07.737 回答
1

要扩展 Nikola Radosavljević 的第一个答案,您可以创建一个这样的扩展方法:

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.Itemas的类型IResponseItem,您可以直接调用DoAction,而无需知道项目的实际(又名具体)类型:

Response.Item.DoAction();

这是多态性- 有一个共同的基类型 ( IResponseItem) 和多个继承/实现类型 (AccountUpdater等),这些类型实现相同的成员 ( DoAction) 来做不同的事情。(MSDN 上 C# 编程指南中的多态性

于 2013-09-09T22:31:30.280 回答
0

您的问题可以通过实现接口或抽象基类来解决。

如果一个类型实现了其他类型,您可以存储在较低类型的变量中。例如,在 .Net 中,每种类型都派生自基本类object。这允许您将 a List、 aString和所有其他类型存储在对象变量中。

类似地,您可以使用接口来存储实现该接口的实例。接口基本上是类需要实现的方法和属性的列表。

在您的情况下,我建议您应该添加更高级别的抽象。例如,您可以创建一个界面

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

您可以在每个响应中实现此接口。

public class EndOfDayResponse : IResponse
{ ... }

的类型Receipt.Item将是 IResponse 而不是对象。然后,您可以检查实际类型,response is EndOfDayResponse然后进行适当的转换。

于 2013-09-09T22:30:39.577 回答