将基本类型转换为派生类型

8

我正在通过继承现有的.NET框架类来扩展它。如何将基础类型的对象转换为派生类型?

public class Results { //Framework methods }

public class MyResults : Results { //Nothing here }

//I call the framework method

public static MyResults GetResults()
{
    Results results = new Results();
    //Results results = new MyResults(); //tried this as well.

    results = CallFrameworkMethod();

    return (MyResults)results; //Throws runtime exception
}

我理解这是因为我试图将一个基本类型转换为派生类型,如果派生类型有附加属性,则不会分配内存。当我添加额外的属性时,我并不在乎它们是否初始化为null。

我如何在不进行手动复制的情况下完成这个操作?


1
你是否可以创建一个扩展方法,而不是扩展这个类型? - Chris Haas
这是一个好主意。使用扩展方法,我可以附加新的方法。但我不能真正地向类型添加额外的属性/变量。 - Nick
这似乎是很费力的方式,只为了避免编写简单的复制构造函数。我知道在很多情况下这是不可能的,但在这种情况下似乎是一个可能的解决方案。 - overslacked
@overslacked - 拷贝构造函数应该怎么写?你能给我一些指导吗? - Nick
6个回答

12
你不能这样做。如果“results”没有引用到一个“MyResults”(例如,如果“CallFrameworkMethod”返回一个基本的“Results”实例),那么强制转换也无法改变它:你需要创建一个新的“MyResults”,基于现有的非“MyResults”。类型转换是关于改变引用的编译时类型,而不是改变所引用对象的具体类型。
你可以使用Reflection或AutoMapper等工具来帮助初始化新的“MyResults”对象--但必须有一个新的“MyResults”对象,因为你不能告诉一个基本的“Results”对象变成一个“MyResults”对象。

这与方差/协方差有关吗?我在使用.NET 4.0,我知道他们为方差/协方差做了一些事情。有什么想法吗? - Nick
不,C# 4.0的差异性与您在泛型接口或委托的类型参数中使用基类/派生类类型有关;而且您这里没有任何泛型接口或委托。您的问题更根本:它涉及对象的具体类型。结果对象根本不是 MyResults 类型的实例。如果 CallFrameworkMethod 返回一个(实际)Results,对该对象的引用不能转换为 MyResults,因为该对象不是 MyResults。唯一的解决方法是将 CallFrameworkMethod 更改为返回 MyResults,或创建一个新的 MyResults。 - itowlson

1

这样怎么样:

...
MyResults results  = new MyResults();
...

你可能还需要在你的MyResults类中创建一个构造函数:

public class MyResults : Results
{
    public MyResults() : base() {}
}

“什么是‘nothing here’的确切含义?”

编辑

 results = (CallFrameworkMethod() as MyResults);

它不会抛出异常,但如果它对你有用 - 这取决于你想要进一步做什么...


什么也没有。目前,我已经从基础类型继承,以便将来添加新属性。 - Nick
我明白了。我编辑了我的答案 - 请检查新的解决方案。 - Gacek
@Gacek - 使用 as 关键字不会抛出异常,但是 MyResult 对象为空,尽管 CallFrameworkMethod 返回一个非空对象。 - Nick
关于您的编辑:如果CallFrameworkMethod返回的是“Results”而不是“MyResults”,那么您的编辑将只会将“results”设置为null。强制转换仍然失败,但“as”运算符通过返回null而不是抛出异常来指示这一点。 - itowlson
Results类中是否有一个构造函数,允许基于另一个对象创建一个对象?类似于Results(Results referenceObject)这样的构造函数?如果是这样的话,您可以在您的类中实现类似的构造函数(通过派生原始构造函数),然后像下面这样调用它:var results = new MyResults(CallFrameworkMethod()); - Gacek
很遗憾,我查看了框架中的Results实现,它没有这个功能。 - Nick

1

正如之前的回答所说,您需要实例化一个新的MyResults实例,然后将属性从您的Results对象复制过来。您问什么是“复制构造函数” - 它只是一个构造函数,它接受一个对象并用它来填充正在构造的对象。例如:

    public class Results
    {
        public string SampleProperty1 { get; set; }
        public string SampleProperty2 { get; set; }
    }

    public class MyResults : Results
    {
        public MyResults(Results results)
        {
            SampleProperty1 = results.SampleProperty1;
            SampleProperty2 = results.SampleProperty2;
        }
    }

拷贝构造函数通常比使用以下代码更方便、易读和可重用:

MyResults myResults = new MyResults
{
    SampleProperty1 = results.SampleProperty1,
    SampleProperty2 = results.SampleProperty2
};

如果有很多属性和/或您对类进行了很多更改,您可以使用反射(例如 C# 使用反射复制基类属性)或像 AutoMapper(http://automapper.codeplex.com)这样的工具来复制属性。但通常情况下,这可能会过度。


1
Results results = new MyResults();
   ...

return (MyResults)results;

应该可以工作。如果不能,那么问题就在别的地方。


1
不行,因为他随后使用CallFrameworkMethod的返回值覆盖了它 -- 这可能返回一个与MyResults不同的东西,否则这个强制类型转换就成功了。 - itowlson
他创建了一个 MyResult 类的实例,然后调用 CallFrameworkMethod 方法,该方法将某些无法转换的其他实例分配给结果对象。 - Machta

1

不,你不能避免将内容复制到派生类型的实例中。如果你可以将CallFrameworkMethod更改为泛型方法,并且MyResults具有零参数构造函数,则CallFrameworkMethod可以直接创建您的派生类型的新实例,然后仅使用父类型的成员。

但是,最终你可能不得不复制到一个新对象中。请记住,这个复制代码肯定可以在另一个方法中重用,你不必在需要它的每个地方都重新编写它。


复制构造函数会是什么样子?你能给我一些指针吗? - Nick
C#没有“复制构造函数”。但是您可以编写一个可重用的方法,该方法接受基类并返回派生类的实例,并复制所有基类成员。或者您是在询问通用思想? - Ben Voigt

0
要将一个对象向下转换为其派生类,该对象必须在其派生类中“出生”。 例如: 我有一个简单的名为Shape的类 这是Shape类的代码。
public class Shape{
     public void draw(){}
}

然后我有另一个名为Circle的类,如下所示:

public class Circle:Shape{
      public void drawCircle(){}
}

现在,如果我创建一个超类的对象,比如:

Shape noShape = new Shape();

如果尝试将这个noShape对象向下转换为Circle对象,就像这样

Circle circle = (Circle) noShape; 

它将生成一个异常。这是因为circle从未作为Shape出现过。 要执行此向下转换功能,noShape必须被诞生为Circle对象。可以通过以下方式实现:

Circle initialShape = new Circle();

现在,您可以将此initialShape对象简单地向上转型为Shape对象。就像这样。
Shape upcastedObject = initialShape;

现在你可以将对象upcastedObject向下转型为其派生类,如下所示:

Circle derivedCircle = (Circle) upcastedObject;

现在,derivedCircle对象的行为将像一个Circle对象一样。因为它是作为Circle对象生成的。我使用BORN这个词,请记住这一点。 做这样的事情或者进行这样的用例取决于您的需求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接