我有一种情况,我从我收到的数据创建一个名为EntryEvent
的对象。必须解析该数据。基本事件应该启动对通过构造函数接收的数据的解析,并将其提供给对象。子类型知道如何分析该特定数据集。现在,在编译所述代码时,我收到警告CA2214,它包含一个虚拟方法的调用链。虽然可能会产生不可预见的后果,但我不知道如何获得所需的行为:解析收到的事件而无需调用额外的" Parse"来自外部的方法。
有问题的守则是:
public abstract class BaseEvent
{
protected BaseEvent(object stuff)
{
this.ParseEvent();
}
protected abstract void ParseEvent();
}
public class EntryEvent : BaseEvent
{
public EntryEvent( object stuff )
: base( stuff )
{
}
protected override void ParseEvent()
{
// Parse event
}
}
答案 0 :(得分:2)
根据MSDN(重点是我的):
调用虚方法时,直到运行时才会选择执行该方法的实际类型。当构造函数调用虚方法时,可能是调用该方法的实例的构造函数未执行。
所以在我看来你有这些选择(至少):
1)不要禁用该警告,但要禁止特定类记录其预期行为的消息(假设您特别注意处理此类情况)。如果它在一个非常受控制的环境中仅限于少数课程(毕竟......警告不是错误而且可能被忽略),那就不那么糟了。
2)从基类构造函数中删除该虚方法调用,但在那里留下abstract
方法声明。开发人员必须实现这样的方法并在构造函数中调用它们,他们需要将它们的类标记为sealed
。最后在类/方法文档中添加一个必须在其构造函数中调用该方法的方法,并且它们的类必须是sealed
才能这样做。
他们可以忘记该调用但您可以在访问属性或方法时添加(对于DEBUG
版本)检查(例如,强制,作为类接口的一部分,设置特定标志) 。如果他们忘记设置标志或忘记调用该方法,则会引发异常("尚未构建此对象,必须在派生类构造函数中调用ParseEvent()。" )。
我不太喜欢这种方法,因为它增加了额外的复杂性但是如果你的类层次结构太大(那时你觉得你不能使用#1)或者懒惰的初始化(在#3中描述)不适用那么它可能是一个工作解决方案。我还考虑更改设计以引入一个工厂方法,该方法将为每个完全构造的对象调用ParseEvent()。
3)改变你的设计:将解析推迟到需要的时候。例如:
public abstract class BaseEvent
{
public DateTime TimeStamp
{
get
{
if (_timestamp == null)
ParseEvent();
return _timestamp.Value;
}
protected set { _timestamp = value; }
}
protected BaseEvent(object stuff)
{
}
protected abstract void ParseEvent();
private DateTime? _timestamp;
}
最后一个示例仅用于说明目的,您可能希望使用Lazy<T>
以更加一致,清晰且线程安全的方式执行相同的任务。当然,实际上你会有更多的字段/属性,并且可能解析将一次性提供所有值(那么你只需要一个标记,每个字段不需要Nullable
/特殊值)这是方法即使它更冗长,我也更喜欢。