C# 为什么不在构造函数中调用可重写的方法?
这是一个过于简化的示例,但我有一些实际代码在概念上做了相同的事情(尝试验证派生类的值“set”访问器方法),并且分析器给我“不要调用构造函数中的可重写方法”。我试图弄清楚是应该更改代码,还是忽略警告。我想不出任何理由应该听从这个警告C# 为什么不在构造函数中调用可重写的方法?,c#,methods,constructor,overriding,C#,Methods,Constructor,Overriding,这是一个过于简化的示例,但我有一些实际代码在概念上做了相同的事情(尝试验证派生类的值“set”访问器方法),并且分析器给我“不要调用构造函数中的可重写方法”。我试图弄清楚是应该更改代码,还是忽略警告。我想不出任何理由应该听从这个警告 public abstract class SimpleUrl { protected string _url; public abstract string Url { get; set; } public SimpleUrl()
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();
}
}
}
答案确实是,这可能会导致意外行为 代码中缺少的内容:
public class HttpUrl : SimpleUrl
{
public HttpUrl()**:base()**
{ }
public HttpUrl(string Url)**:base(Url)**
{
this.Url = Url;
}
.........
}
你现在明白了吗?
你不能让构造函数暴露你的基。然后,在设置虚拟成员之前,base将执行其构造函数 正如T.S.所说(谢谢T.S.),在实例化派生类型时,将始终调用基构造函数。如果您像我在问题中所做的那样,在派生构造函数没有明确指定要使用哪个基构造函数的情况下,则使用无参数基构造函数
public HttpUrl() // : base() is implied.
及
因此,这个问题的完整答案是:如果密封了派生类,则基类中声明的抽象或虚拟方法只能在基类中重写。因此,要生成干净的代码,您不需要在基本构造函数中运行这一行:
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();
}
}
}
或者,如果只密封可重写的方法(使其不再可被任何其他派生类重写),则不需要密封整个派生类
我想补充一点,构造函数中的调用和重写方法会使程序处于不一致的状态。如果您的方法抛出异常会发生什么?那么你的目标永远不会被构建。在构造函数中捕获这些异常不是一个好的实践
ctor()
{
method(); //throws an exception
}
从Windows窗体可以学到的一个教训是,设计器具有从构造函数调用的InitializeComponents
public MyView: System.Windows.Forms.Form
{
public MyView()
{
InitializeComponent();
}
}
InitializeComponent由设计器生成。不要修改它
因为更改设计器属性时,更改将丢失。
InitializeComponent的目的只是让设计师将所有
它的代码用于设置所有属性,以及在需要时读取的位置
绘制设计器曲面,以便渲染相关的
组件设置
如果InitializeComponent是一个重写方法呢?然后您可以修改它,最后,如果您的更改错误并破坏基类上的逻辑,则整个表单可能处于不一致的状态原因是子类可能在超类尚未初始化的方法中使用对象的属性,从而导致意外行为。Shoot。一分钟前,我发布了一个答案,这是错误的。所以我把它删掉了。以防万一有人注意到了。@EdwardNedHarvey我注意到了,并且仍然可以看到:-PBolding在源代码中不起作用-你只需要使用内联注释。我知道。这只是为了让某人注意到特定的部分-看起来像你那样:o)@T.S.谢谢你,我现在知道了。你的回答很有帮助,但不是一个完整的答案。我将为这个问题添加另一个答案。再次感谢您的帮助。@EdwardNedHarvey只是在谷歌上搜索“调用构造函数中的虚拟成员”之类的内容。为什么不这样做有不同的例子。要点-这不是犯罪,但如果你使用这种技术,你可能会得到扭曲的结果。事实上,在某些设计中,您可以接受这一点,因为使用对象继承的方式。我记得有一个应用程序充满了它,但从来没有出现过任何问题,因为它是经过设计完成的。@T.S.是的,在发布这个问题之前,我已经在谷歌上搜索过,并阅读了msdn。但答案从来都不清楚。我在问题中所写的代码似乎可以安全地使用,但我错了。现在我有了真正的答案。再次感谢你的帮助。
ctor()
{
method(); //throws an exception
}
public MyView: System.Windows.Forms.Form
{
public MyView()
{
InitializeComponent();
}
}