你的构造函数中的合同没有得到满足。由于你正在引用一个对象的字段(this.data),其他线程可能会访问该字段,并在假设和第一个参数解析以及第三个参数解析之间更改其值。(例如,它们可能是三个完全不同的数组。)
你应该将数组分配给一个局部变量,然后在整个方法中使用该变量。然后分析器将知道约束条件已经得到满足,因为没有其他线程有能力更改该引用。
var localData = this.data;
if (localData == null) return;
byte[] newData = new byte[localData.Length]; // Or whatever the datatype is.
Array.Copy(localData, newData, localData.Length); // Now, this cannot fail.
这样做不仅满足了限制条件,而且在许多情况下,实际上使代码更加强壮。
希望这能帮助你找到答案。我无法直接回答你的问题,因为我没有可以使用静态检查器的 Visual Studio 版本。(我用的是 VS2008 Pro)。我的答案基于我自己对代码的视觉检查,看起来静态合约检查器使用了类似的技术。我很感兴趣!我需要得到一个。:-D
更新:(接下来有很多猜测)
经过思考,我认为我可以很好地猜测可以证明什么或不能证明什么(即使没有访问静态检查器)。如其他答案所述,静态检查器不进行过程间分析。因此,面临可能出现多线程变量访问的情况(如 OP 中),静态检查器只能有效地处理本地变量(如下所定义)。
所谓“本地变量”,是指任何其他线程都无法访问的变量。这将包括在方法中声明的任何变量或作为参数传递的变量,除非参数被装饰为 ref
或 out
,或者该变量被捕获在匿名方法中。
如果本地变量是值类型,则其字段也是本地变量(依此类推)。
如果本地变量是引用类型,则只有引用本身而不是它的字段可以被视为本地变量。即使在方法中构造的对象也是如此,因为构造函数本身可能会泄露对构造的对象的引用(例如向静态集合进行缓存)。
只要静态检查器不执行任何过程间分析,任何关于不符合上述定义的非本地变量的变量的假设都可能随时失效,因此在静态分析中被忽略。
例外 1:由于字符串和数组在运行时被认为是不可变的,因此它们的属性(如 Length)可以被分析,只要字符串或数组变量本身是本地变量即可。这不包括其他线程可变的数组内容。
例外 2:数组构造函数可能在运行时被认为不会泄漏对构造的数组的引用。因此,在方法体内构建且未泄漏到方法之外(作为参数传递给另一个方法、分配给非本地变量等)的数组具有元素也可被视为本地变量。
这些限制似乎相当严厉,我可以想象几种改进的方法,但我不知道已经完成了什么。以下是静态检查器理论上可以完成的其他一些事情。有这个工具的人应该检查已经完成了什么,还有哪些没有完成:
- 此功能可以确定构造函数是否泄漏了任何对对象或其字段的引用,并将所构造的任何对象的字段视为局部变量。
- 可以对其他方法进行无泄漏分析,以确定传递给方法的引用类型在该方法调用后是否仍然可以被视为局部变量。
- 使用ThreadStatic或ThreadLocal修饰的变量可以被视为局部变量。
- 可以提供选项来忽略使用反射修改值的可能性。这将允许将引用类型上的私有只读字段或静态私有只读字段视为不可变。另外,当启用此选项时,仅在
lock(X){ /**/ }
结构内部访问的一个私有或内部变量X且未泄漏的情况下,该变量可以被视为局部变量。但是,这些事情实际上会降低静态检查器的可靠性,因此有点棘手。
另一个可能会开启许多新分析的可能性是将变量和使用它们的方法(以及递归地如此)声明地分配到特定的唯一线程。这将是语言的重大改进,但可能值得一试。