【问题标题】:Create new object from instance of another从另一个实例创建新对象
【发布时间】:2013-09-13 11:50:30
【问题描述】:

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

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

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

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

现在当我执行receipt.Item 时,我得到了返回的类型;但是由于使用 Web 服务调用设置接口的方式,除非我键入 cast 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 的返回对象时,我可以通过执行 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;
}

【问题讨论】:

  • 很难理解你的问题。您是否正在尝试创建该类的新实例?如果是,为什么不在 switch 语句中创建它?
  • 另外,不同的响应类型是否相似,以便您可以创建每个响应类型都实现的接口?在这种情况下,您可以尝试将您的方法实现为泛型方法并使用 default 关键字。
  • 它们在某种意义上是相似的,一些(可能是 3 个)返回类型值是相同的,但绝大多数(超过 15 个)不是。另外@Dilshod 我正在尝试提出更好的解释。
  • 那么响应类型没有实现相同的变量或函数?每个响应类型中都没有变量(比如说 StatusCode)?另外,您是否需要响应类型来对其执行功能?还是您只对类型感兴趣?在这种情况下,您可以将其替换为 Enum。
  • 你想如何使用你的类生成的结果?

标签: c# .net


【解决方案1】:

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

您正在尝试创建对现有实例的类型化引用。你已经有一个对象的实例,保存在一个类型为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 编辑器写的,所以可能不会马上运行 :)

【讨论】:

  • 多态,根据我的理解,并没有真正使用它,是它用于对象非常相似,不是吗?这些对象大约有 3 个共同值,其余的根据响应类型而有所不同。在这种情况下,这似乎不太合适。不过,您的解决方案可能就是我要走的路。
  • @Brett 这种糟糕的设计,当一种方法处理来自服务器的所有不同答案时,它是唯一可能的解决方案。
  • @Brett 请参阅我的回答,了解多态在这里如何发挥作用。
  • @Brett,您设置了约束,所有这些不同的类型都应由一种方法处理。查看更详细的答案。
【解决方案2】:

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

Response.Item.DoAction();

这是多态性 - 有一个共同的基类型 (IResponseItem) 和多个继承/实现类型 (AccountUpdater 等) 实现相同的成员 (DoAction) 来做不同的事情事物。 (Polymorphism in the C# Programming Guide on MSDN)

【讨论】:

  • 这是一种更好的通用方法。但是,Reponse.Item 似乎是一个数据模型类,它并不总是放置业务逻辑的好地方。
【解决方案3】:

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

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

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

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

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

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

public class EndOfDayResponse : IResponse
{ ... }

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

【讨论】:

  • 如果所讨论的变量是IResponse 类型或object 类型有什么区别?特定的类型检查仍然是必要的。接口提供了哪些附加值?现在,如果界面有一个方法,其中包含要对响应项执行的操作,那就另当别论了。
  • 这真的取决于其余的代码。一方面,我个人认为额外的接口使代码更具可读性。此外,如果他在其他地方使用 StatusCode 之类的东西,他不必使用类型检查/强制转换。
  • 至于你的第二点,他想在设计时访问成员 -- 因为当我做 responseObject.Item 我无法访问不同的变量对象类型 -- 所以他必须进行类型检查并转换为新变量。
猜你喜欢
  • 1970-01-01
  • 2016-08-25
  • 2014-12-05
  • 2011-04-14
  • 2019-10-14
  • 2023-04-02
  • 2018-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多