如何在C#/ .Net中表示线性类型?

时间:2011-06-22 07:00:43

标签: .net linear-types

是否有合理的方式在.Net(Compact Framework / desktop 3.5公共子集)中表达linear type的概念,以这样的方式:(a)所需的语法不会变得过于冗长,复杂的,或其他痛苦的(b)不变量可以在运行时强制执行,也可以在编译时通过代码分析进行验证(因此,一个全力以赴的维护程序员不能轻易忽略不变量)?这里的想法是避免在子系统边界处对command objects进行防御性复制。

3 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分: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,然后在运行时捕获其值设置且未使用。

答案 2 :(得分:0)

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

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

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

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

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