从另一个实例创建新对象

时间:2013-09-09 21:48:51

标签: c# .net

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

  1. Dynamically create an object of <Type>
  2. Get a new object instance from a Type
  3. - 希望能更好地描述问题吗?----

    当我调用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;
    }
    

3 个答案:

答案 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。这允许您在对象变量中存储ListString和其他所有类型。

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

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

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

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

public class EndOfDayResponse : IResponse
{ ... }

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