I have the following class:
public class AndSpecification<TEntity> : ISpecification<TEntity>
{
protected ISpecification<TEntity> First { get; private set; }
protected ISpecification<TEntity> Second { get; private set; }
public bool IsSatisfiedBy(TEntity entity)
{
return First.IsSatisfiedBy(entity) && Second.IsSatisfiedBy(entity);
}
public AndSpecification(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
Contract.Requires(first != null);
Contract.Requires(second != null);
Contract.Ensures(First != null);
Contract.Ensures(Second != null);
First = first;
Second = second;
}
}
Note that I use Contract.Ensures()
to ensure that after the constructor is invoked, First
and Second
are never null. However, Code Contracts give me a warning in IsSatisfiedBy()
method implementation: CodeContracts: Possibly calling a method on a null reference 'this.First'
(same for the Second
). Can't figure out what's wrong, is that me or the static checker?
It appears you are missing some contracts and object invariants. I took your code above and ran Code Contracts static analysis on it and got exactly what you got. Then I worked my way through the code to arrive at an implementation that satisfies the static analyzer.
[ContractClass(typeof(ISpecificationContracts<>))]
public interface ISpecification<TEntity>
where TEntity : class
{
bool IsSatisfiedBy(TEntity entity);
}
[ContractClassFor(typeof(ISpecification<>))]
abstract class ISpecificationContracts<TEntity>
: ISpecification<TEntity> where TEntity : class
{
public bool IsSatisfiedBy(TEntity entity)
{
Contract.Requires(entity != null);
throw new NotImplementedException();
}
}
public class AndSpecification<TEntity>
: ISpecification<TEntity> where TEntity : class
{
private readonly ISpecification<TEntity> _first;
private readonly ISpecification<TEntity> _second;
protected ISpecification<TEntity> First {
get
{
Contract.Ensures(Contract.Result<ISpecification<TEntity>>() != null);
return _first;
}
}
protected ISpecification<TEntity> Second
{
get
{
Contract.Ensures(Contract.Result<ISpecification<TEntity>>() != null);
return _second;
}
}
public bool IsSatisfiedBy(TEntity entity)
{
return First.IsSatisfiedBy(entity) && Second.IsSatisfiedBy(entity);
}
public AndSpecification(ISpecification<TEntity> first,
ISpecification<TEntity> second)
{
Contract.Requires(first != null);
Contract.Requires(second != null);
Contract.Ensures(_first == first);
Contract.Ensures(_second == second);
_first = first;
_second = second;
}
[ContractInvariantMethod]
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "Required for code contracts.")]
private void ObjectInvariant()
{
Contract.Invariant(_first != null);
Contract.Invariant(_second != null);
}
}