假设以下简单代码:
public class Foo // : IFoo
{
private string _field;
public string Property
{
get { return _field; }
}
private void SetField()
{
_field = " foo ";
}
private string Method()
{
SetField();
return Property.Trim();
}
}
静态检查器能够证明Property
使用它时Method
不会为空。
现在,我引入一个接口和一个契约,静态检查器开始抱怨:“可能在空引用上调用一个方法'this.Property'。
这是一个错误还是我错过了什么?
带有界面的代码如下所示:
public class Foo : IFoo
{
private string _field;
public string Property
{
get { return _field; }
}
private void SetField()
{
_field = " foo ";
}
private string Method()
{
SetField();
return Property.Trim();
}
}
[ContractClass(typeof(IFooContract))]
public interface IFoo
{
string Property { get; }
}
[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
public string Property
{
get { throw new System.NotImplementedException(); }
}
}
我的设置是这样的:
我得到以下输出:
[...]
C:\{path}\CC2.cs(11,19): message : CodeContracts: Suggested ensures: Contract.Ensures(Contract.Result<System.String>() == this._field);
C:\{path}\CC2.cs(16,13): message : CodeContracts: Suggested ensures: Contract.Ensures(this._field != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(Contract.Result<System.String>() != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(this._field != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(this.Property.Trim() != null);
C:\{path}\CC2.cs(21,13): message : CodeContracts: Suggested ensures: Contract.Ensures(Contract.Result<System.String>() == this.Property.Trim());
[...]
C:\{path}\CC3.cs(33,13): warning : CodeContracts: Possibly calling a method on a null reference 'this.Property'
[...]
我使用Visual Studio 2010 Ultimate和.NET 4作为目标框架。
答案 0 :(得分:1)
不是一个答案,而是对这个问题的一些想法。这不是那种混淆代码合同的接口合同。我设法用简单的例子重现了这个,没有ContractClass
的接口。只需将第二个示例更改为简单
//Foo's declaration
public interface IFoo
{
string Property { get; }
}
你会得到同样的错误。即使在“属性”字段上添加Contract.Assume(_field != null);
也无法修复它(它会修复此问题,将此Assume
添加到SetField
方法)。我没有设法用Invariants抑制空引用异常警告。唯一有用的是一个非常难看的解决方案,你必须为接口契约提供后置条件,并在属性字段中给代码契约提供assume
的提示。完整代码如下所示
public class Foo : IFoo
{
private string _field;
public string Property
{
get
{
Contract.Assume(_field != null);
return _field;
}
}
private void SetField()
{
_field = " foo ";
}
private string Method()
{
SetField();
return Property.Trim();
}
}
[ContractClass(typeof(IFooContract))]
public interface IFoo
{
string Property { get; }
}
[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
public string Property
{
get
{
Contract.Ensures(Contract.Result<string>() != null);
throw new NotImplementedException();
}
}
}
编辑:
由于_field
可以为null,我建议使用此方法体为分析器提供提示,以便它不会打扰null引用警告。
private string Method()
{
SetField();
Contract.Assume(Property != null);
return Property.Trim();
}
P.S。正如约翰·索梅兹在pluralsight training regarding Code contracts所说的那样“静态分析是一项复杂而神秘的任务,如果没有暗示分析员采用假设方法调用,就很难解决这个问题”。
答案 1 :(得分:0)
如果问题只出现在类中的代码中 - 就像我的例子中的情况那样 - 实用的解决方案很简单:
使用支持字段而不是属性:
public class Foo : IFoo
{
private string _field;
public string Property
{
get { return _field; }
}
private void SetField()
{
_field = " foo ";
}
private string Method()
{
SetField();
return _field.Trim();
}
}