我可以使用代码约定来定义接口上的只读,不变属性吗?即实例化后总是产生相同值的属性?
答案 0 :(得分:4)
首先注释.NET中的术语:
现在回到你的问题。
所有属性getter都在.NET Code Contracts中隐式标记为“Pure”。这意味着从吸气剂中读取应该永远不会产生明显的副作用。
严格来说,如果您的抽象接口只有只读属性,那么整个接口被认为是只读的。
但是,听起来你真正想要的是一种将接口标记为不可变的方法,并让底层类继承该状态。不幸的是,没有办法做到这一点,抽象接口只能添加功能。 Code Contracts可以做的最好的事情就是确保正确添加功能。
摘要
不,它不支持。
答案 1 :(得分:2)
这是一个可能的解决方案,作为概念证明。它有各种各样的问题,尤其是所有对象都将在缓存中保持活动状态,我们正在使用扩展方法有效地欺骗代码契约框架,以允许我们维护状态,但是它至少表明这种合同测试是可能的。
下面的代码定义了各种各样的东西:
IRuntimeProperty
的接口AlwaysTheSame
,它返回一个整数。我们不关心价值是什么,但希望它总是返回相同的东西。RuntimePropertyExtensions
,用于定义扩展方法IsAlwaysTheSame
,该方法使用缓存来自IRuntimeProperty
个对象的先前结果。RuntimePropertyContracts
,它调用扩展方法来检查AlwaysTheSame
的返回值。GoodObject
的类AlwaysTheSame
,因此它始终为给定对象返回相同的值。BadObject
的类AlwaysTheSame
,因此连续调用会返回不同的值。Main
方法。以下是代码:
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace SameValueCodeContracts
{
[ContractClass(typeof(RuntimePropertyContracts))]
interface IRuntimeProperty
{
int AlwaysTheSame { get; }
}
internal static class RuntimePropertyExtensions
{
private static Dictionary<IRuntimeProperty, int> cache = new Dictionary<IRuntimeProperty, int>();
internal static bool IsAlwaysTheSame(this IRuntimeProperty runtime, int newValue)
{
Console.WriteLine("in IsAlwaysTheSame for {0} with {1}", runtime, newValue);
if (cache.ContainsKey(runtime))
{
bool result = cache[runtime] == newValue;
if (!result)
{
Console.WriteLine("*** expected {0} but got {1}", cache[runtime], newValue);
}
return result;
}
else
{
cache[runtime] = newValue;
Console.WriteLine("cache now contains {0}", cache.Count);
return true;
}
}
}
[ContractClassFor(typeof(IRuntimeProperty))]
internal class RuntimePropertyContracts : IRuntimeProperty
{
public int AlwaysTheSame
{
get
{
Contract.Ensures(this.IsAlwaysTheSame(Contract.Result<int>()));
return default(int);
}
}
}
internal class GoodObject : IRuntimeProperty
{
private readonly string name;
private readonly int myConstantValue = (int)DateTime.Now.Ticks;
public GoodObject(string name)
{
this.name = name;
Console.WriteLine("{0} initialised with {1}", name, myConstantValue);
}
public int AlwaysTheSame
{
get
{
Console.WriteLine("{0} returning {1}", name, myConstantValue);
return myConstantValue;
}
}
}
internal class BadObject : IRuntimeProperty
{
private readonly string name;
private int myVaryingValue;
public BadObject(string name)
{
this.name = name;
Console.WriteLine("{0} initialised with {1}", name, myVaryingValue);
}
public int AlwaysTheSame
{
get
{
Console.WriteLine("{0} returning {1}", name, myVaryingValue);
return myVaryingValue++;
}
}
}
internal class Program
{
private static void Main(string[] args)
{
int value;
GoodObject good1 = new GoodObject("good1");
value = good1.AlwaysTheSame;
value = good1.AlwaysTheSame;
Console.WriteLine();
GoodObject good2 = new GoodObject("good2");
value = good2.AlwaysTheSame;
value = good2.AlwaysTheSame;
Console.WriteLine();
BadObject bad1 = new BadObject("bad1");
value = bad1.AlwaysTheSame;
Console.WriteLine();
BadObject bad2 = new BadObject("bad2");
value = bad2.AlwaysTheSame;
Console.WriteLine();
try
{
value = bad1.AlwaysTheSame;
}
catch (Exception e)
{
Console.WriteLine("Last call caused an exception: {0}", e.Message);
}
}
}
}
它输出如下:
good1 initialised with -2080305989 good1 returning -2080305989 in IsAlwaysTheSame for SameValueCodeContracts.GoodObject with -2080305989 cache now contains 1 good1 returning -2080305989 in IsAlwaysTheSame for SameValueCodeContracts.GoodObject with -2080305989 good2 initialised with -2080245985 good2 returning -2080245985 in IsAlwaysTheSame for SameValueCodeContracts.GoodObject with -2080245985 cache now contains 2 good2 returning -2080245985 in IsAlwaysTheSame for SameValueCodeContracts.GoodObject with -2080245985 bad1 initialised with 0 bad1 returning 0 in IsAlwaysTheSame for SameValueCodeContracts.BadObject with 0 cache now contains 3 bad2 initialised with 0 bad2 returning 0 in IsAlwaysTheSame for SameValueCodeContracts.BadObject with 0 cache now contains 4 bad1 returning 1 in IsAlwaysTheSame for SameValueCodeContracts.BadObject with 1 *** expected 0 but got 1 Last call caused an exception: Postcondition failed: this.IsAlwaysTheSame(Contract.Result())
我们可以根据需要创建尽可能多的GoodObject
个实例。在他们身上调用AlwaysTheSame
将始终满足合同。
相反,当我们创建BadObject
个实例时,我们只能在每个上调用AlwaysTheSame
;一旦我们第二次调用它,合同就会抛出异常,因为返回的值与我们上次得到的值不一致。
正如我在开始时所说的那样,我对在生产代码中使用这种方法持谨慎态度。但我同意这是一个有用的东西,想通过契约来指定,如果在代码契约框架中内置了对这种运行时返回值不变性的支持,那就太棒了。