从另一个对象的实例创建新对象

本文关键字:对象 新对象 创建 一个对象 实例 | 更新日期: 2023-09-27 18:30:53

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

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

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

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

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


有一个总体返回类型,即来自我正在调用的 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;
}

从另一个对象的实例创建新对象

我会

在回答之前尝试重申您的问题。

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

为了扩展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)来做不同的事情。(MSDN 上的 C# 编程指南中的多态性)

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

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

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

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

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

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

public class EndOfDayResponse : IResponse
{ ... }

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