【问题标题】:Dynamic Casting C#动态铸造 C#
【发布时间】:2011-12-01 00:39:38
【问题描述】:

我有父类 X 和父类 H。

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

然后我从 X 派生一个子类 x1,从 H 派生一个子类 h1。

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

但是 x1 尚未在其父类 X 中定义的方法和属性将可供 h1 使用。

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

【问题讨论】:

  • A、B、C、D、E、F、G....等等。你能发布一些代码吗?你的解释很难理解。
  • 它是 2 个父类,每个都有一个派生的子类。贴出代码再简单不过了。
  • '2 个父类,每个都有一个派生的子类'也不能让它变得简单,不幸的是。
  • 等等。您说 h1 可以接受 X 的实例。为什么要将 X 实例强制转换为 x1?你会得到一个运行时错误。
  • 我个人知道,将发送给 h1 的构造函数的参数将是 x1 类型。由于这是在父类级别定义的,因此代码确实允许传递 X 类型。

标签: c# dynamic casting


【解决方案1】:

让我们首先将问题改写为易于理解的内容。而不是 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
    }
}

你的问题是:

我怎样才能使“最爱”在“猴子”中永久地成为“香蕉”类型?

简短的回答是:你不能。该字段属于给定类型,并且可以包含对 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
    }
}

【讨论】:

    【解决方案2】:

    没有所谓的“永久”演员表(或者,更确切地说,“永久性演员表”的想法是荒谬的,因为这意味着演员表对对象做了一些事情,它没有,所以没有什么可以持久或撤消)。你的变量是H 类型,变量的类型永远不会改变。您的实例是H1 类型,并且实例的类型永远不会改变。转换只是告诉编译器“是的,即使我只是将此实例引用为H 类型,但我实际上知道它是H1 类型,因此将它存储在任何可以引用@ 实例的变量中都是安全的987654325@。”

    如果你的结构是这样的:

    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
    

    【讨论】:

    • 我认为你把 X 和 H 颠倒了。 “H 的构造函数需要 X 类型的实例”
    • @DennisPalmer:看来我做到了。尽管如此,我认为我所得到的已经足够清楚了。问题中的口头描述有点难以理解。
    【解决方案3】:

    如果我理解正确,你有以下课程。

    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)的构造函数,您可以确保它始终被正确设置。

    【讨论】:

      【解决方案4】:

      这就是泛型的用途。如果我正确理解您的要求,您的 H 类应该在 C 派生自 X 的某些“C”(类 H)上是通用的,以便可以编写 H 以与 X 一起使用,但是 H1 可以是定义为从 H 派生,以便它可以与 X1 一起使用。

      编辑:当然,根据这些类的复杂程度,将 H 设为泛型可能不值得。在那种情况下,我会选择马特迪拉德的答案。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 这可能是个好建议,但是 OP 在H1 类中的IX1 接口也会遇到同样的问题。 IX1 中将有其他方法/属性不在IX 中。
        【解决方案6】:

        我不认为你可以。

        如果在 h1 的构造函数中给定了 x1,则可以将 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;
            }
        }
        

        【讨论】:

          【解决方案7】:

          如果您的目标是 .Net 4.0,那么您可以使用 here 中描述的新动态类型。它在功能上允许你做你想做的事,但从设计的角度来看,它与编译时强类型检查的概念背道而驰。

          【讨论】:

            【解决方案8】:

            对我来说,这听起来像是一个狡猾的设计。听起来您遇到了“Parallel Class Heirarchies”问题。

            如果没有更多细节,我无法给出更具体的建议。一种可能对您有所帮助的设计模式称为“桥”。但是,很多比我聪明的人都写了很好的一般性建议,所以我要指给你看他们的书:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-11-21
              • 1970-01-01
              • 2019-02-28
              • 1970-01-01
              相关资源
              最近更新 更多