动态铸造c#

本文关键字:动态 | 更新日期: 2023-09-27 18:16:18

我有父类X和父类h

H在其字段数据中有一个对类型X的引用。H的构造函数需要一个X类型的实例,然后将其存储在引用中。

我从X得到子类x1,从H得到子类h1。

h1将在其构造函数中接受X或x1的实例。

但是没有在父类X中定义的x1的方法和属性将可用于h1。

如何永久地将x1转换为类h1中的x1类型?

动态铸造c#

我们先把这个问题改写成容易理解的内容。不叫X和H,我们叫它们食物和动物。您的场景是:

abstract class Animal
{
    protected Food favourite;
    protected Animal(Food f) { this.favourite = f; }
}
abstract class Food
{
}
sealed class Banana : Food 
{
    public void Peel() {}
}
sealed class Monkey : Animal
{
    public Monkey(Banana banana) : base(banana) {}
    public PeelMyBanana()
    {
        this.favourite.Peel(); // Error, favourite is of type Food, not Banana
    }
}

你的问题是:

我怎样才能永久地使' favorite '在'Monkey'里面的类型为'Banana' ?

简短的回答是:你不能。该字段是给定类型的,并且可以包含对Food类型实例的任何引用。编译器和运行时不知道或不关心您是否碰巧知道对实际分配给字段的东西类型有更强的限制。

有很多方法可以解决这个问题。您可以添加一个为您执行转换的访问器:

sealed class Monkey : Animal
{
    public Monkey(Banana banana) : base(banana) {}
    private Banana Favourite { get { return (Banana)this.favourite; } }        
    public PeelMyBanana()
    {
        this.Favourite.Peel(); // Works
    }
}

或者你可以泛化基类:

abstract class Animal<F> where F : Food
{
    protected F favourite;
    protected Animal(F f) { this.favourite = f; }
}
abstract class Food
{
}
sealed class Banana : Food 
{
    public void Peel() {}
}
sealed class Monkey : Animal<Banana>
{
    public Monkey(Banana banana) : base(banana) {}
    public PeelMyBanana()
    {
        this.favourite.Peel(); // Legal; Animal<Banana>.favourite is of type Banana
    }
}

没有"永久"强制转换(或者更确切地说,"永久强制转换"的想法是荒谬的,因为这意味着强制转换对对象做了一些事情,而它没有,所以没有什么可以保留或撤销的)。变量的类型是H,变量的类型永远不会改变。您的实例的类型是H1,并且实例的类型永远不会改变。强制类型转换只是告诉编译器"是的,即使我只引用这个实例为H类型,我实际上知道它是H1类型,所以将它存储在任何可以引用H1实例的变量中都是安全的。"

如果你的结构像这样:

class H
{
}
class X
{
    public H HValue { get; private set; }
    public X(H h)
    {
        HValue = h;
    }
}
class H1 : H
{
    public void Foo() { }
}
class X1 : X
{
    public X1(H1 h1) : base(h1)
    {
    }
}

如果你总是想使用h1变量的值而不向下转换,那么你将不得不将该值存储在其他地方。

现在,一个有点"臭"的方式来完成你想要的(不必每次都写强制转换代码),你可以这样做:

class X1 : X
{
    public X1(H1 h) : base(h) { }
    public new H1 HValue 
    { 
        get { return (H1)base.HValue; }
    }
}

这将允许您在任何X1类型的变量中引用X1的地方引用相同的名为HValue的属性。所以…

X x = new X1(new H1());  // x.HValue would be of type H, even though the 
                         // reference itself is H1
X1 x = new X1(new H1()); // x.HValue would be of type H1

如果我理解正确,您有以下类。

class X
{
}
class X1 : X
{
}
class H
{
    H(X x)
    {
        MyX = x;
    }
    X MyX { get; private set; }
}
class H1 : H
{
    H1(X x) : base(x)
    {
    }
}

您希望H1处理MyX,就好像它是更具体的X1类型。

最简单的方法是在H1上添加另一个属性,将x引用为X1:

class H1 : H
{
    H1(X1 x) : base(x)
    {
        MyX1 = x;
    }
    X1 MyX1 { get; private set; }
}

通过在H1上创建一个只接受X1(而不是任何旧的X)的构造函数,您可以确保它总是被正确设置。

这就是泛型的作用。如果我正确理解了你的问题,你的H类应该在某些"C"上是泛型的(类H),其中C派生自X,因此H可以被编写为与X一起工作,但是H1可以被定义为派生自H<x1;>

EDIT:当然,根据这些类的复杂程度,可能不值得为使H泛型而烦恼。在这种情况下,我会选择Matt Dillard的答案。

您可以使用Interface,因此H没有对X的引用,而是对IX的引用,然后Xx1实现IX方法/属性。

我认为你做不到。

您可以向h1添加类型为x1的字段,并在h1的构造函数中设置它,然后使用该字段访问您想要访问的h1中x1的方法(记住x1字段可能未设置)。

从你的描述中不清楚h1构造函数是否看起来像:

h1(X xInstance)

h1(X xinstance, x1 x1Instance)

但我假设第一个,即使你说'h1将在其构造函数中接受X和x1的实例。否则你知道你有一个x1,所以它变得相当简单:)

所以我认为你想这样做:

public class H1: H
{
    private X m_x;
    private X1 m_x1;
    public H1(X x):base(x)
    {
        m_x=x;
        X1 x1 = x as X1;
        if (x1!=null)
            m_x1=x1;
    }
}

如果你的目标是。net 4.0,那么你可以使用这里描述的新的动态类型。它在功能上允许您做您想做的事情,但是从设计的角度来看,它在编译时猛烈地抨击了强类型检查的概念。

对我来说,这听起来像是一个狡猾的设计。听起来你遇到了"并行类继承"的问题。

如果没有更多的细节,我无法给出更具体的建议。一种可能对您有所帮助的设计模式称为"桥"。然而,很多比我聪明得多的人都写过很好的一般性建议,所以我要给你推荐他们的书:

Martin Fowler -重构Joshua Kerievsky -重构模式Kent Beck -实现模式(给出了一个通过重新考虑抽象来解决这个问题的好例子)