71

我知道 C# 得到了很多并行编程支持,但是 AFAIK 仍然没有用于副作用验证的构造,对吗?

我认为既然 C# 已经布局,那就更棘手了。但是有计划把它放进去吗?还是 F# 是唯一具有副作用验证构造的 .NET 语言?

4

3 回答 3

173

语言不是 C#,但框架可能是 .NET。

.NET 4 中引入的 Contracts 库 + 静态分析工具可能会引入这些:

微软现在在 .NET 3.5 框架中使用 [Immutable] 和 [Pure]。

例如,请参阅 System.Core.dll 中 .NET 3.5 中的 [Microsoft.Contracts.Immutable] 和 [Microsoft.Contracts.Pure]。不幸的是,它们是内部的。但是,Microsoft.Contracts.* 主要源于 Spec# 研究,并且 Spec# 已被合并到将成为 .NET 4.0 一部分的 Contracts API 中。

我们将看看这是什么结果。我尚未检查预发布的 .NET 4.0 位是否包含任何 API,例如 Contracts API 中的 [Pure] 或 [Immutable]。如果他们这样做,我想静态分析工具将是执行规则的工具,而不是编译器。

编辑我刚刚从本周最新的 MS 代码合同预发布版本中加载了 Microsoft.Contracts.dll 。好消息:库中存在 [Pure] 和 [Mutability(Mutability.Immutable)] 属性,这表明它们将在 .NET 4.0 中。呜呼!

编辑 2现在 .NET 4 已经发布,我查找了这些类型。[Pure]仍然存在于 System.Diagnostics.Contracts 命名空间中。它不适用于一般用途,而是与 Contract API 的前置条件和后置条件检查一起使用。它不是编译器强制执行的,代码合同检查器工具也没有强制执行纯度。[可变性] 消失了。有趣的是,微软在 .NET 3.5 中使用 Mutability 和 Pure 属性(在 System.Core.dll 的内部 BigInteger 类中),.NET 4 已将 BigInteger 移动到 System.Numerics 中,并剥离了 [Pure] 和 [Mutability]该类型的属性。底线:.NET 4 似乎对副作用验证没有任何作用。

编辑 3最近(2011 年末)预览的 Microsoft Rosyln 编译器即服务工具——据信计划在 Visual Studio 2015 中用于 RTM——看起来他们将能够支持这样的东西;你可以为编译器编写扩展来检查纯度和不变性,如果用这些属性装饰的东西不遵守规则,就会发出编译器警告。即便如此,我们正在寻找几年的时间来支持这一点。

编辑 4既然 Rosyln 于 2015 年夏天就在这里,那么为纯/不变性构建编译器扩展的能力确实存在。但是,这对现有的框架代码和第 3 方库代码都没有任何作用。但即将到来的是针对不可变类型的 C# 7 提案。这将由编译器强制执行,并将在 C# 中引入一个新的不可变关键字,并在 .NET 框架中引入一个 [Immutable] 属性。用法:

// Edit #4: This is a proposed design for C# 7 immutable as of June 2015.
// Compiler will implicitly mark all fields as readonly.
// Compiler will enforce all fields must be immutable types.
public immutable class Person
{
    public Person(string firstName, string lastName, DateTimeOffset birthDay)
    {
        FirstName = firstName; // Properties can be assigned only in the constructor.
        LastName = lastName;
        BirthDay = birthDay; 
    }

    public string FirstName { get; } // String is [Immutable], so OK to have as a readonly property
    public string LastName { get; }
    public DateTime BirthDay { get; } // Date is [Immutable] too.
}

编辑 5现在是 2016 年 11 月,似乎不可变类型已从 C# 7 中删除。C# 8 总是有希望的。:-)

编辑 6现在是 2017 年 11 月。C# 8 即将全面亮相,虽然我们不会有纯函数,但我们会有只读的 structs。这使得结构不可变,从而允许进行多种编译器优化。

编辑 7现在是 2020 年 7 月,C# 9 将支持记录,它们是完全不可变的类型。此外,记录将具有With用于从现有记录创建新记录以表示新状态的表达式。

编辑 8现在是 2021 年 11 月,C# 10 已发布,支持With结构和结构的表达式record。这些也有助于创建不可变类型。

于 2009-02-26T23:41:33.523 回答
17

不仅没有任何副作用验证 - 甚至没有验证类型是不可变的,这是沿着 IMO 同一路线的一个较小的步骤。

我不相信在 C# 4.0 中会有任何事情发生(尽管我很容易出错)。我真的希望不变性对 C# 5.0 产生影响;当然,Eric Lippert 已经写了很多关于它的博客,MS 的人们一直在考虑并行性。

抱歉,这不是更令人鼓舞的画面。

编辑:Judah 的回答要好得多……框架支持对您来说是否足够好?:)(如果代码合同的某些方面还没有为 .NET 4.0 做好准备,我不会完全感到惊讶 - 如果他们将初始版本保持在相对较小的范围内并在以后对其进行提升。)

于 2009-02-26T23:33:54.820 回答
14

原则上,验证某些东西是否不可变以及代码是否缺少副作用很容易。类/数据结构的所有字段必须是只读的,并且它们的类型必须是另一个不可变对象。我们还需要一种将委托标记为“纯”(无副作用)的方法,但这可能都是可能的。

然而,问题是这通常过于严格。在 F# 中,您通常会以无副作用且不可变的风格编写代码,但在本地使用一些突变通常是有益的。这不会破坏整体的纯度(在某种意义上),并使编写代码更容易。但是,自动验证这一点很困难(这意味着这是一个有趣的理论问题......)

例如,以“纯”方式处理数组是非常好的。您可以使用Array.map 之类的方法,将某些函数应用于所有元素并返回一个数组,而无需修改原始数组。该函数会在返回(新创建的)数组之前对其进行变异,但该数组不会在其他任何地方发生变异,因此这原则上是pure,但很难验证(这在 F# 中是非常有用的编程模式)。

所以,我认为可以做很多事情,但简单地禁止所有副作用可能不像看起来那么好。合约的好处是它们也可能在这种情况下使用。

于 2009-02-28T14:52:04.447 回答