Почему не вызывать переопределяемые методы в конструкторах?
Это упрощенный пример, но у меня есть реальный код, который концептуально делает то же самое (пытается проверить значения "установить" методы доступа производных классов), и анализатор дает мне "не вызывать переопределяемые методы в конструкторах."Я пытаюсь понять, должен ли я изменить свой код или игнорировать предупреждение. Не знаю, почему я должен прислушаться к предупреждению.
public abstract class SimpleUrl
{
protected string _url;
public abstract string Url { get; set; }
public SimpleUrl()
{ }
public SimpleUrl(string Url)
{
this.Url = Url;
}
}
public class HttpUrl : SimpleUrl
{
public HttpUrl()
{ }
public HttpUrl(string Url)
{
this.Url = Url;
}
public override string Url
{
get
{
return this._url;
}
set
{
if (value.StartsWith("http://"))
this._url = value;
else
throw new ArgumentException();
}
}
}
3 ответов
как сказал T. S. (Спасибо T. S.), базовый конструктор всегда будет вызываться при создании экземпляра производного типа. Если вы сделаете так, как я сделал в вопросе, где конструктор производных не явно укажите, какой базовый конструктор использовать, затем используется базовый конструктор без параметров.
public HttpUrl() // : base() is implied.
и
public HttpUrl(string Url) // : base() is still implied.
// Parameterless. Not :base(Url)
так что полный ответ на этот вопрос: абстрактные или виртуальные методы, объявленные в базовом классе только переопределяемым в базовом классе, если вы загерметизированная производного класса. Поэтому, чтобы сделать чистый код, вы не запускаете эту строку в базовом конструкторе:
this.Url = Url; // Don't do this in the base constructor
вместо этого вы должны "запечатать" производный класс (или метод). Следующим образом:
public abstract class SimpleUrl
{
protected string _url;
public abstract string Url { get; set; }
// Optionally declare base constructors that do *not* call Url.set
// Not sure why these constructors are optional in the base, but
// required in the derivative classes. But they are.
public SimpleUrl()
{ }
}
public sealed class HttpUrl : SimpleUrl
{
public HttpUrl() // Not sure why this is required, but it is.
{ }
public HttpUrl(string Url)
{
// Since HttpUrl is sealed, the Url set accessor is no longer
// overridable, which makes the following line safe.
this.Url = Url;
}
public override string Url
{
get
{
return this._url;
}
set
{
if (value.StartsWith("http://"))
this._url = value;
else
throw new ArgumentException();
}
}
}
кроме того, вам не нужно запечатывать весь производный класс, если вы просто запечатываете переопределяемый метод (что делает его более не переопределяемым любыми другими производными классами)
public class HttpUrl : SimpleUrl
{
// ...
public override sealed string Url
// ...
ответ действительно есть, это может привести к неожиданному поведению.
http://msdn.microsoft.com/en-us/library/ms182331.aspx
то, что вы пропустили в своем коде:
public class HttpUrl : SimpleUrl
{
public HttpUrl()**:base()**
{ }
public HttpUrl(string Url)**:base(Url)**
{
this.Url = Url;
}
.........
}
теперь вы видите? Вы можете не иметь конструктора ваш дно. И затем base выполнит свой конструктор, прежде чем вы установите свой виртуальный член.
Я хочу добавить, что вызов и переопределение метода в конструкторе могут оставить вашу программу в несогласованном состоянии. Что произойдет, если ваш метод выдает исключение? Тогда ваш объект никогда не будет построен. И это не хорошая практика, чтобы поймать эти исключения в конструкторе.
ctor()
{
method(); //throws an exception
}
урок, который вы можете узнать из Windows Form, заключается в том, что конструктор имеет InitializeComponents, который вызывается из конструктора.
public MyView: System.Windows.Forms.Form
{
public MyView()
{
InitializeComponent();
}
}
InitializeComponent создается конструктором. Не изменяйте его потому что изменения будут потеряны при изменении свойств конструктора. Цель InitializeComponent является исключительно для дизайнера, чтобы положить все его код для установки всех свойств и где-то для его чтения, когда рисовать поверхность конструктора, для того чтобы представить уместное настройки компонентов
что, если InitializeComponent был методом переопределения? Затем вы можете изменить его и в конце, вся форма может быть в непоследовательном состоянии, если ваши изменения неверны и нарушают логику в базовом классе