使用链式构造函数避免代码分析 CA2000 警告?
Posted
技术标签:
【中文标题】使用链式构造函数避免代码分析 CA2000 警告?【英文标题】:Avoiding code analysis CA2000 warning with chained constructors? 【发布时间】:2011-07-19 17:14:28 【问题描述】:避免 CA2000 警告关于 non-disposed locals 的正常模式是使用一个临时变量,如果出现任何问题,该变量就会被释放,例如:
Foo f = null;
try
f = new Foo();
Foo result = f;
f = null;
return result;
finally
if (f != null)
f.Dispose();
有点冗长,但它有效,而且很有意义。但是如何将该模式应用于链式构造函数,如下所示:
public HomeController ( IDataRepository db )
this.repo = db ?? new SqlDataRepository();
public HomeController ( )
: this(new SqlDataRepository())
此代码抛出两个 CA2000 警告,每个构造函数一个。第一个我可以摆脱使用临时变量模式。这很烦人,因为本地在构建之后但在分配给成员字段之前没有任何方法超出范围,稍后会清理。所以我不知道 CA 的问题是什么,但至少我知道如何解决它。
但是,据我所知,没有任何替代方法可以编写第二个构造函数调用来引入 try/finally。分配给的字段是只读的,因此必须在构造函数中设置。 C# 不允许你在任何地方调用链式构造函数,而是在构造函数体之前。而且,在这两个警告中,第二个警告实际上更合理——对链式构造函数的调用可能(理论上,如果它确实做了任何实际工作)会抛出异常,并且不会处理新构造的参数。
当然,我总是可以直接隐藏此消息(CA2000 似乎需要很多),但如果有消除问题的实际方法,我更愿意这样做。
【问题讨论】:
IMO,这是一个 CA 错误。 【参考方案1】:我无法在采用 IDataRepository 参数的构造函数上重现 CA2000 违规。鉴于此,再加上您对两个构造函数使用相同的“默认值”这一事实,可以避免示例场景的 CA2000 问题的最简单更改是:
public HomeController(IDataRepository db)
this.repo = db ?? new SqlDataRepository();
public HomeController()
: this(null)
显然,如果您的第一个构造函数不接受空参数值,这将不会很好地工作。如果是这种情况,并且您完全同意仅在一个位置设置相应字段的想法,那么您仍然可以选择避免 CA2000,例如调用更智能的私有构造函数。例如:
public HomeController(IDataRepository db)
: this(() => db, false)
if (db == null)
throw new ArgumentNullException("db");
public HomeController()
: this(() => new SqlDataRepository(), true)
private HomeController(Func<IDataRepository> repositoryRetriever, bool disposeOnFailure)
IDataRepository repository = repositoryRetriever.Invoke();
try
this.repo = repository;
catch
if (disposeOnFailure)
repository.Dispose();
throw;
就我个人而言,我认为以上内容是非常令人讨厌的 hack,特别是考虑到它涉及增加代码复杂性和运行时异常的可能性,以避免一开始并不严重的潜在问题。我的建议是简单地忽略此类潜在的 CA2000 违规行为,除非以下两项均属实:
-
在对象的实例化和将其分配给字段的方法结束之间确实有可能发生非崩溃异常。
未能处置孤立实例的后果相当严重(例如:使文件处于锁定状态)。
【讨论】:
不应该在try
块内调用repositoryRetriever
吗?
@ChrisWue:不,没有必要。在将一次性值分配给目标变量之前,无论如何您都无法释放它,因为您没有对它的引用。以上是关于使用链式构造函数避免代码分析 CA2000 警告?的主要内容,如果未能解决你的问题,请参考以下文章
代码分析警告 CA2000:在对象“new ContainerControlledLifetimeManager()”上调用 Dispose