14

是否有一种合理的方式来表达 .Net(Compact Framework/desktop 3.5 通用子集)中的线性类型的概念,使得 (a) 所需的语法不会变得过于冗长、复杂或其他痛苦和(b) 不变量既可以在运行时强制执行,也可以在编译时通过代码分析进行验证(所以一心一意的维护程序员不能随便忽略不变量)?这里的想法是避免在子系统边界处对命令对象进行防御性复制的需要。

4

3 回答 3

1

线性类型,基于线性逻辑理论,与唯一性类型密切相关,是分配给具有属性的值的类型,即它们始终只有一个引用。这些对于描述大的不可变值(如文件、字符串等)很有价值。

不可变类型是其内部状态在实例化后无法更改的类型。

“深度不可变”类型是其依赖图包含的引用类型也是“深度不可变”的类型。如果依赖引用类型本身不是“深度不可变”,则该类型被称为“浅不可变”。

在 C# 中,我们使用引用类型和值类型。引用类型的实例可以在不同的并发执行的代码之间共享,而值类型是堆栈绑定的(除非装箱),在共享时复制,因此是自治的,尽管不是不可变的(并且可能包含对其他引用类型的依赖关系,然后“复制共享” )。

尽管共享引用类型的能力无疑是面向对象框架的一个强大功能,但在企业开发的世界中,它也应被视为其主要弱点之一,并应极其谨慎地使用。任何无法以原子方式执行的事情都会暴露脆弱性,并有机会交错错误以间歇性地造成严重破坏。

在 C# 中,我们能做的最好的事情就是描述我们的意图。可以通过将类型的整个内部状态标记为私有和只读来部分实现不变性。无法强制执行深度不变性(也不能执行浅层不变性),因此由开发人员决定是否坚持意图。对状态的更改是通过返回包含请求状态的类型的新实例的静态方法。

public sealed class PersonImmutable {

    private readonly int _age;
    private readonly string _name;

    public PersonImmutable(int age, string name) { 
        this._age = age;
        this._name = name;
    }

    public int Age {
        get { return this._age; }
    }

    public string Name {
        get { return this._name; }
    }

    public static PersonImmutable NotifyBirthday(PersonImmutable source) {
        return new PersonImmutable(1 + source.Age, source.Name);
    }
}
于 2011-08-18T09:16:38.117 回答
1

.Net 中有两种类型:引用类型和值类型。

当您通过将引用类型分配给另一个变量来复制引用类型时,只会复制引用。

复制值类型时,会逐字节复制该类型的全部内容。

在这两种情况下,都无法阻止、修改或获取有关它的通知(与 C++ 的复制构造函数相比)。这意味着您无法在 .Net 中实现线性类型。

正如其他人建议的那样,您可以改用不可变(或可冻结)类型。

于 2011-08-18T20:57:29.587 回答
0

提供的链接确实定义了 LinearVariable,它的定义类似于:

Option Explicit On
Option Strict On
Option Infer On 

<System.Diagnostics.DebuggerDisplay("{_state}: {_value}")> _
Class LinearVariable(Of T)
  Private Enum State
   Unassigned
   Assigned
   Used
 End Enum
 Private _state As State = State.Unassigned
 Private _value As T
 Public Sub New()
  'Allow creation and later assignment
 End Sub
 Public Sub New(ByVal Value As T)
  _value = Value
  _state = State.Assigned
 End Sub
 Public Shared Widening Operator CType(Value As T) As LinearVariable(Of T)
  Return New LinearVariable(Of T)(Value)
 End Operator
 Public Shared Widening Operator CType(Value As LinearVariable(Of T)) As T
  Return Value.Value
 End Operator
 Public Property Value As T
  Get
   If _state = State.Assigned Then
    _state = State.Used
#If DEBUG Then
    Return _value
#Else ' Release - free the reference immedately after use
    value = _value
    _value = Nothing
#End If
   End If
   If _state = State.Unassigned Then _
    Throw New NullReferenceException("LinearVariable is unassigned")
   If _state = State.Used Then _
    Throw New AccessViolationException("LinearVariable has already been accessed")
   Throw New InvalidOperationException
  End Get
  Set(ByVal Value As T)
   ' May want to check _state, although the "definition" at http://c2.com/cgi/wiki?LinearTypes seems to allow multiple writes
   _value = Value
   _state = State.Assigned
  End Set
 End Property
End Class

(这已经编译但未经测试。)

这显然只在运行时有效。我想不出任何尝试.Value在编译时强制使用的方法。

请注意,LinearVariable IDisposable当它的值已设置但未使用时,您可以在运行时进行然后捕获。

于 2011-06-22T18:47:40.963 回答