2008-12-02 4 views
49

У меня есть два класса, Foo и Bar, которые имеют конструктор, как это:Вызов ПЕРЕОПРЕДЕЛЕНЫ Конструктор и базовый конструктор в C#

class Foo 
{ 
    Foo() 
    { 
     // do some stuff 
    } 

    Foo(int arg) 
    { 
     // do some other stuff 
    } 
} 

class Bar : Foo 
{ 
    Bar() : base() 
    { 
     // some third thing 
    } 
} 

Теперь я хочу, чтобы ввести конструктор для бара, который принимает Int, но я хочу материал, который происходит в Bar() для запуска , а также как материал из Foo (int). Что-то вроде этого:

Bar(int arg) : Bar(), base(arg) 
{ 
    // some fourth thing 
} 

Есть ли способ сделать это в C#? Лучшее, что я до сих пор делаю, делает работу Bar() функцией, которая также вызывается Bar (int), но это довольно неэлегантно.

ответ

23

Нет, это невозможно. Если вы используете Reflector для изучения IL, созданного для каждого конструктора, вы поймете, почему - вы в конечном итоге вызываете оба конструктора для базового класса. Теоретически, компилятор мог построить скрытые методы для выполнения того, что вы хотите, но действительно нет никакого преимущества перед вами, делая то же самое в явной форме.

+1

Вероятно, это было связано с тем, что вы не дали решения проблемы, хотя, очевидно, это одно. – 2009-03-13 07:56:53

1

У вас нет конструктора Bar, который принимает int, вызывают конструктор без параметров?

+0

Он также хочет вызвать параметризованный конструктор базового класса. – 2008-12-02 20:31:07

+0

Хорошо, теперь я вижу. – 2008-12-02 20:48:50

1

Можете ли вы поместить материал из Bar() в Bar (int) и вызвать Bar (int) с помощью Bar() со значением по умолчанию? Тогда Bar (int) может вызвать базовый конструктор.

class Bar : Foo 
{ 
    Bar() : this(0) 
    { 
    } 

    Bar(int arg) : base(arg) 
    { 
    } 
} 

Это точно не отвечает на ваш вопрос, но в зависимости от вашего сценария может оказаться приемлемым решением.

32

Я бы повторен цепь Конструкторы, поэтому они называются как

Bar() : this(0) 
Bar(int) : Foo(int) initializes Bar 
Foo(int) initializes Foo 
Foo() : this(0) 

Это подходит, если без параметров конструкторы приобретают какое-то значение по умолчанию для параметра INT другого конструктора. Если конструкторы не связаны друг с другом, вы, вероятно, делаете что-то не так с вашим типом, или, может быть, нам нужна дополнительная информация о том, чего вы пытаетесь достичь.

3

Это единственное, что я могу думать ...

public class Foo 
{ 
    public Foo() 
    { 
    } 
    public Foo(int? arg): this() 
    { 
    } 

} 
public class Bar : Foo 
{ 
    private int x; 
    public Bar(): this(new int?()) // edited to fix type ambiguity 
    { 
     // stuff that only runs for paramerless ctor 
    } 
    public Bar(int? arg) 
     : base(arg) 
    { 
     if (arg.HasValue) 
     { 
      // Do stuff for both parameterless and parameterized ctor 
     } 
     // Do other stuff for only parameterized ctor 
    } 
} 
+0

У вас есть +1 для идеи с нулевым значением, но она имеет некоторый поток - если вы добавите еще один ctor с одним аргументом (типа класса, а не struct), он будет терпеть неудачу, так как теперь этот (null) не будет знать, какой из них выберите. – 2008-12-02 20:40:27

12

Я рекомендовал бы изменить свой конструктор цепи, чтобы перейти от наименее характерных для наиболее специфичны.

class Foo 
{ 
    Foo() 
    { 
     // do some stuff 
    } 

    Foo(int arg): this() 
    { 
     // do some other stuff 
    } 
} 

class Bar : Foo 
{ 
    Bar() : Bar(0) 
    { 
     // some third thing 
    } 

    Bar(int arg): base(arg) 
    { 
     // something 
    } 
} 

Любое создание объекта Bar теперь вызовет все 4 конструктора. Цепочка конструктора должна обеспечивать значения по умолчанию для более конкретных конструкторов, а не наоборот. Вы должны действительно посмотреть на то, что вы пытаетесь выполнить, и убедиться, что вы делаете, имеет смысл. Курт прав, что есть технические причины, по которым вы не можете этого сделать, но есть и логические причины, почему вы не должны этого делать.

1

Вы можете взять код инициализации для Bar() и сделать его методом и вызвать его из обоих конструкторов, а новый конструктор просто вызвать base (arg)?

0

Вы можете использовать этот код:

public Foo 
{ 
    public Foo() 
    { 
     this.InitializeObject(); 
    } 

    public Foo(int arg) : this() 
    { 
     // do something with Foo's arg 
    } 

    protected virtual void InitializeObject() 
    { 
     // initialize object Foo 
    } 
} 

public Bar : Foo 
{ 
    public Bar : base() { } 

    public Bar(int arg) : base(arg) 
    { 
     // do something with Bar's arg 
    } 

    protected override void InitializeObject() 
    { 
     // initialize object Bar 

     base.InitializeObject(); 
    } 
} 

Просто переопределить метод InitializeObject() так же, как в приведенном выше коде, и поместить весь код, который вы хотите поместить в параметр меньше конструктору там. И, наконец, позвоните base.InitializeObject() в конце кода.

Надеюсь, это полезно.