我有一种情况,我从我收到的数据创建一个名为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
}
}
最佳答案 根据MSDN(重点是我的):
When a virtual method is called, the actual type that executes the method is not selected until run time. When a constructor calls a virtual method, it is possible that the constructor for the instance that invokes the method has not executed.
所以在我看来你有这些选择(至少):
1)不要禁用该警告,但要禁止特定类记录其预期行为的消息(假设您特别注意处理此类情况).如果它在一个非常受控制的环境中仅限于少数几个类别(毕竟……警告不是错误而且可能被忽略),这并不是那么糟糕.
2)从基类构造函数中删除该虚方法调用,但在那里留下抽象方法声明.开发人员必须实现这样的方法,并在构造函数中调用它们,他们需要将它们的类标记为密封.最后在类/方法文档中添加一个必须在其构造函数中调用该方法的方法,并且必须密封它们的类才能这样做.
他们可以忘记该调用,但您可以在访问属性或方法时添加(对于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 /特殊值)这是我更喜欢的方法,即使它更冗长.