Как я могу сказать наследующему классу, чтобы он не вызывал конструктор без параметров своего базового класса? - PullRequest
11 голосов
/ 16 июля 2010

Я был удивлен, узнав, что конструктор без параметров моего базового класса вызывается каждый раз, когда я вызываю любой конструктор в производном классе.Я подумал, что именно для этого : base() явно вызовет базовый конструктор, если и когда я захочу.

Как предотвратить вызов базового конструктора при создании экземпляра производного класса?

using System;

namespace TestConstru22323
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer("Jim", "Smith");
            Customer c = new Customer();
            Console.WriteLine(customer.Display());

            Console.ReadLine();
        }
    }

    public class Person
    {
        public Person()
        {
            Console.WriteLine("I don't always want this constructor to be called. I want to call it explicitly.");
        }
    }

    public class Customer : Person
    {
        private string _firstName;
        private string _lastName;

        //public Customer(string firstName, string lastName): base()  //this explicitly calls base empty constructor
        public Customer(string firstName, string lastName) //but this calls it anyway, why?
        {
            _firstName = firstName;
            _lastName = lastName;
        }

        public string Display()
        {
            return String.Format("{0}, {1}", _lastName, _firstName);
        }
    }
}

Ответы [ 7 ]

25 голосов
/ 16 июля 2010

Единственный способ - указать ему, какой другой базовый ctor вы хотите вызвать; что, конечно, означает, что вы должны выбрать некоторый базовый ctor для вызова.

Вы не можете заставить его вообще не вызывать базовый ctor - концептуально , построение Customer выполняется сначала путем построения Person, а затем Customer специфическая конструкция поверх него. Например, предположим, что Person имеет private полей - как они будут правильно построены, если для построения Customer было разрешено не сначала создать Person?

8 голосов
/ 16 июля 2010

В .NET каждый конструктор объекта в иерархии объектов будет вызываться независимо от того, вызываете ли вы :base() или нет.

:base() вызывается неявно, если вы не вызываете его явно.

:base() можно использовать, если вы хотите вызвать другой конструктор для родительского объекта, а не конструктор по умолчанию.

Если у вас есть код в родительском конструкторе, который не следует вызывать каждый раз, может быть лучше перенести этот код в собственный метод, который необходимо будет вызывать явно после создания объекта. Или создайте параметризованный конструктор для родительского объекта и используйте этот параметр, чтобы определить, должен ли код выполняться или нет.

Например:

:base(true) - This executes your code.
:base(false) - This does not execute your code.
2 голосов
/ 16 июля 2010

Как уже отмечали другие, производный экземпляр должен вызывать вызов одного из конструкторов его базового класса.

Если вы хотите контролировать выполнение логики инициализации базового класса, удалите его конструктор по умолчанию и заменитеэто примерно так:

public class Base {
    // Base has no default constructor
    public Base(bool initialize) {
        if (initialize) {
            // ... logic here 
        }
    }    
}

И производные конструкторы выглядят так:

// Derived1 executes the initialization logic
public Derived1(): base(true) {}

// Derived2 does not
public Derived2(): base(false) {}
0 голосов
/ 16 июля 2010

Когда вы создаете экземпляр класса, создаются все классы в иерархии наследования, начиная с самого верхнего Parent до самого нижнего потомка, останавливаясь на том типе, который вы создали.Поэтому, если вы создаете экземпляр System.Text.StringBuilder (), сначала вы вызываете конструктор по умолчанию для System.Object, затем StringBuilder ().Вы можете использовать ключевое слово base для вызова другого конструктора (с равным или меньшим количеством параметров), но не с большим количеством параметров.Если вы не укажете base, вызывается конструктор по умолчанию, поэтому причина, по которой это происходит с вами.Вы также можете использовать ключевое слово base в контексте метода экземпляра для вызова базовых реализаций, например, в случае шаблона декоратора.Если вы не хотите вызывать частные конструкторы базового класса, вам следует установить дочерние элементы с закрытыми конструкторами по умолчанию, а также явно вызывать base (params) для других ваших конструкторов.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class ProcessCaller
    {
        static void Main()
        {
            MyDerived md1 = new MyDerived(1);
            object o = new System.Text.StringBuilder();//This will implicitly instantiate the classes in the inheritance hierarchy:  object, then stringbuilder
        }
    }

public class MyBase
{
   int num;

   public MyBase() 
   {
      Console.WriteLine("in MyBase()");
   }

   public MyBase(int i )
   {
      num = i;
      Console.WriteLine("in MyBase(int i)");
   }

   public virtual int GetNum()
   {
      return num;
   }
}

public class MyDerived: MyBase
{
   //set private constructor.  base(i) here makes no sense cause you have no params
   private MyDerived()
   {

   }

    // Without specifying base(i), this constructor would call MyBase.MyBase()
   public MyDerived(int i) : base(i)
   {
   }
   public override int GetNum()
   {
       return base.GetNum();//here we use base within an instance method to call the base class implementation.  
   }
}

}
0 голосов
/ 16 июля 2010

Вы можете сделать базовый конструктор по умолчанию protected, а затем иметь только конструкторы не по умолчанию для базы и ее потомка.

редактировать

Вы могли бы дать базовому конструктору protected с другой подписью (например, защищенный тип перечисления) и поместить туда код инициализации, в то время как конструктор по умолчанию, также protected, не имеет конкретной инициализации.

0 голосов
/ 16 июля 2010

Ваше требование вызывать конструктор только для некоторых производных объектов не соответствует объектно-ориентированным принципам. Если это Персона, то это должно быть построено как таковое.

Если вам нужна общая инициализация для некоторых объектов, то вам следует рассмотреть возможность создания метода Initialize или добавления параметризованного конструктора, который будет вызываться конкретными производными объектами.

Подход параметризованного конструктора выглядит немного неловко:

public abstract class Person
{
    protected Person()
    {
    }

    protected Person( bool initialize )
    {
        if (initialize)
        {
            Initialize();
        }
    }

    // ...
}

public class Customer : Person
{
    public Customer(string firstName, string lastName)
    {
       // ...
    }
}

public class Employee : Person
{
    public Customer(string firstName, string lastName) : base(true)
    {
       // ...
    }
}
0 голосов
/ 16 июля 2010

Одним из способов было бы сделать ваш базовый конструктор по умолчанию закрытым, но это работает только в том случае, если вы делаете вспомогательный конструктор в базовом классе, который вызывает частный конструктор, когда он вам нужен явно.

class Base
{
  private Base() { ... special default base logic ... }
  protected Base(... params ...) : this() { ... exposed base that calls private cons ... }
  protected Base(... other params ...) /* no default call */ { ... exposed cons that doesnt call default ...}
}

class DerivedNoCall
{
  public DerivedNoCall() : base(... other params ...) {} //<-- will NOT call default base
}

class DerivedDoCall
{
  public DerivedDoCall() : base(... params ...) {} //<-- WILL call default base cons indirectly
}

Этодействительно надуманный, и у @ aakashm есть лучший ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...