抑制或避免警告 CA2214

Posted

技术标签:

【中文标题】抑制或避免警告 CA2214【英文标题】:Suppressing or avoiding Warning CA2214 【发布时间】:2014-09-10 12:35:00 【问题描述】:

我有一种情况,我根据收到的数据创建了一个名为 EntryEvent 的对象。必须解析该数据。基本事件应该开始解析通过构造函数接收并提供给对象的数据。子类型知道如何解析该特定数据集。现在,在编译所述代码时,我收到警告 CA2214,它包含对虚拟方法的调用链。虽然产生不可预见的后果可能很糟糕,但我不知道如何获得所需的行为:解析接收到的事件,而无需从外部调用额外的“解析”方法。

有问题的代码是:

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
    

【问题讨论】:

你能给我们更多的背景信息吗?有多种方法可以解决这个问题,但不会导致问题,但哪种方法最好取决于上下文。想象一个不需要需要覆盖ParseEvent的子类是否合理?每个子类实际上是否有一个静态方法,该方法被赋予数据,解析它,然后调用构造函数来获取已经解析的数据? 在您问题的代码中,ParseEvent() 可以是非虚拟的,仅为EntryEvent 定义,并直接从EntryEvent 的构造函数中调用。如果您的真实代码无法做到这一点,您能否更新您的问题以更好地说明您正在处理的场景? @JonSkeet 该事件来自外部来源。每个子类都需要重写,因为每个子类都需要进行自己的解析。基类保存每个事件提供的一般数据。我的意图是强制特定事件的实现者实现解析器,而不是强制他实际调用它。我想要避免的是给实现者(即使是我)随机命名解析方法的选项,不管它是静态的还是非静态的,但也许我过度分析了它。 @private_meta:构造一个事件而不解析任何东西是否有意义,即使这主要用于测试?尚不清楚解析是否真的固有存在于事件对象中,或者它是否只是通常发生的情况。考虑制作获取原始数据的构造函数(即使它们只是私有/受保护的),而不是在构造函数中解析 in。如果有用的话,我可以举个例子。 @JonSkeet 构造函数中提供的数据是通过 c++ 设备库从设备获取事件数据的句柄,因此其目的是不必在事件之外进行设备处理。跨度> 【参考方案1】:

根据 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/特殊值)这是我更喜欢的方法即使它更冗长。

【讨论】:

谢谢。至于 3),延迟加载不是一种选择,因为数据可能在需要时无法从源获得。当然,我想避免 1),因为我看到了潜在的问题。 2) 似乎是一种有效的折衷方案,尤其是将文档与开发人员必须实现的抽象方法相结合。我只是想避免子类的开发者忘记或忽略某些东西。 @private_meta with 2) 开发人员可能会忘记在他的类构造函数中调用 ParseEvent()。他的错,但您可以在访问属性时进行一些检查(至少在 DEBUG 构建中)。也许是一个工厂类和一个 Parse() 方法被那个调用......)

以上是关于抑制或避免警告 CA2214的主要内容,如果未能解决你的问题,请参考以下文章

代码生成器生成文件的代码分析 - 如何抑制警告?

如何抑制“避免使用捆绑版的 Google Play 服务 SDK”警告?

使用链式构造函数避免代码分析 CA2000 警告?

如何解决 CA2202:避免生成 System.ObjectDisposedException 警告 [重复]

抑制 MatPlotLib 良性警告 [重复]

抑制 JSHint / JSLint 或替代服务中的“混合空格和制表符”警告?