【问题标题】:Inheritance in c# questionc#中的继承问题
【发布时间】:2010-08-16 19:10:37
【问题描述】:

我有一些关于 C# 继承的基本问题。

我有两节课。我希望子类继承基类的所有成员(例如 x 和 y),但似乎使用继承我应该初始化基构造函数。如果我在子类中定义另一个 x 和 y 以便稍后初始化基本构造函数,那么继承有什么用?解决办法是什么?

另一个问题是我在基类和子类中有两个具有相同签名的方法,我需要同时使用这两个方法。我能做什么?

class Base
{
    int x;
    int  y;
    string name;

    public Base(int i,int j)
    {
        x = i;
        y = j;
        name="s";
    }
}

class Child
{
    public Base()
    {

    }
}

【问题讨论】:

    标签: c# inheritance constructor


    【解决方案1】:

    你的问题不是很清楚,代码无效[现在由编辑器修复]但我会回答我认为你问的问题。

    要在派生类中初始化基类的字段,必须从派生类的构造函数中调用基类构造函数:

    class Base
    {
        private int x;
        private int y;
    
        // ...
    
        public Base(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    
    // option 1, pass in values as parameters to constructor:
    class Child1
    {
        public Child(int x, int y)
            : base(x, y) // this passes the values up to the base constructor
        {
        }
    }
    
    // option 2, pass literals to the base constructor:
    class Child2
    {
        public Child()
            : base(3, 4) // this passes the literal values 3 and 4 to the base constructor
        {
        }
    }
    
    // elsewhere
    
    var child1 = new Child1(1, 2); // child1.x = 1, child1.y = 2
    var child2 = new Child2(); // child2.x = 3, child2.y = 4
    

    现在,关于您的第二个问题,即在派生类中具有同名的方法。如果你想重新定义一个方法的行为,你应该在基类中将其设置为虚拟,并在派生类中覆盖它,你可以从本例中名为SomeAction的方法中看到:

    class Base
    {
        public virtual void SomeAction()
        {
            Console.Out.WriteLine("Base.SomeAction");
        }
    
        public void DifferentAction()
        {
            Console.Out.WriteLine("Base.DifferentAction");
        }
    }
    
    class Derived
    {
        // this is a normal override
        public override void SomeAction()
        {
            Console.Out.WriteLine("Derivde.SomeAction");
        }
    
        // this is an advanced technique which you should try to avoid in most cases
        public new void DifferentAcion()
        {
            Console.Out.WriteLine("Derived.DifferentAction");
        }
    }
    
    // elsewhere
    var base = new Base();
    base.SomeAction(); // prints Base.SomeAction
    base.DifferentAction(); // prints Base.DifferentAction
    
    var derived = new Derived();
    derived.SomeAction(); // prints Derived.SomeAction
    derived.DifferentAction(); // prints Derived.DifferentAction
    
    ((Base) derived).SomeAction(); // prints Derived.SomeAction
    ((Base) derived).DifferentAction(); // prints Base.DifferentAction
    

    如果一个方法被覆盖,那么无论您是作为基类型还是派生类型引用它,都会显示新的行为。

    (如果您使用new 关键字,或者不添加任何内容,则会创建一个新的继承链,并且行为将取决于您将对象引用为基类型还是派生类型。您可以看到这在上面代码的最后两个调用中:对于被覆盖的方法,使用派生行为,但对于使用“新”的方法,当对象被视为 Base 时,会显示原始行为。)

    【讨论】:

      【解决方案2】:

      使用继承的主要原因是你可能有很多类,它们的功能基本上与超类相同。通过使它们成为超类的子类,您可以将字段和方法放在超类中,然后由所有子类继承,从而允许您只编写此类方法和字段一次,而不是多次编写有子类。

      【讨论】:

        【解决方案3】:

        不要在子类中定义 x 和 y。如果 Child 从 Base 继承,它们就已经存在了。

        【讨论】:

          【解决方案4】:

          基类构造函数总是在您实例化派生类时调用。如果基类有一个默认构造函数,如果你省略对基类构造函数的显式调用,就会调用它。

          例如

          class Base
          {
            int x;
            int y;
          
            //default constructor
            public Base()
            {
               x = -1;
               y = -1;
            }
          
            public Base(int x, int y)
            {
               this.x = x;
               this.y = y;
            }
          }
          
          class Derived
          {
             public Derived()
             {
             }
          }
          

          这将在创建 Derived 对象时编译并设置派生的实例字段为 -1。 但是,如果您省略默认构造函数,您将收到与您描述的类似的编译错误。现在编译器无法自动生成对基本构造函数的调用。我会知道用哪些参数来调用它

          base(x=??,y=??)
          

          通常,您应该多次声明具有相同名称的公共字段/属性,这几乎总是一种强烈的设计气味。 如果基类有一个受保护的、内部的或公共的字段/属性,那么派生类也有(内部有一些奇怪的情况)。对于方法,您可以隐藏 mthod 的基类实现或覆盖它。后者更常见,它要求基本定义是抽象的或虚拟的 例如

          //this requires Base to be declared abstract as well
          public abstract void AbstractMethod(); 
          public virtual void VirtualMethod(){}
          

          这使您可以在 DErived 类中编写如下

          public override void AbstractMethod(){}
          public override void VirtualMethod(){}
          

          如果您需要隐藏某个方法,您可以这样做,尽管通常不鼓励这样做(原因见下文) 如果您只是重新声明该方法,编译器会给您一个警告,但您可以使用 new 告诉编译器您实际上想要隐藏该方法,它将如下所示:

          public new void SomeHiddenMethod(){}

          这如何(可能)对使用该类型的对象产生巨大影响

          public void SomeMEthod(Derived d)
          {
            //this calls the implementation in Derived
            d.SomeHiddenMethod();
            Base b = d;
            //this calls the implementation in Base
            b.SomeHiddenMethod(); 
          }
          

          因此,隐藏方法使得推理代码变得非常困难,因为在同一个对象上调用相同的方法可能会产生不同的结果,即使对象在两次调用之间没有改变状态,而仅仅是因为声明的类型变量改变了。

          最后一点关于继承你应该避免继承功能,简而言之,这只是因为你需要在两个(不相关的)类中具有相同的功能,你不应该创建一个基类来保存该功能,而是你应该创建第三个类以及该类的注入和实例(或类似方法),在设计未密封的类的公共接口时,您应该多考虑Liskov substitutional principle

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-05-11
            • 2011-09-01
            • 1970-01-01
            • 1970-01-01
            • 2014-10-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多