基于线性逻辑理论的线性类型与唯一类型密切相关,是分配给具有以下特性的值的类型:它们始终只有一个引用。这对于描述大型不可变值(如文件、字符串等)非常有价值。
不可变类型是指在实例化后其内部状态无法更改的类型。
“深度不可变”类型是指其依赖图包含的引用类型也是“深度不可变”的类型。如果依赖的引用类型本身不是“深度不可变”的,则该类型被称为“浅不可变”。
在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);
}
}
.Net 中有两种类型:引用类型和值类型。
当你通过将引用类型赋值给另一个变量来复制它时,只会复制引用。
当你复制值类型时,整个类型的内容都会被逐字节复制。
在这两种情况下,无法防止、修改或获得有关它的通知(与 C++ 的复制构造函数相反)。这意味着你不能在 .Net 中实现线性类型。
你可以使用不可变(或可冻结)类型,正如其他人建议的那样。
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
,然后在其值被设置但未使用时在运行时捕获。
LinearVariable
的别名(实际上它应该仅存在于变量声明中,而不是作为例程参数),同样地保留.Value
的内容,尽管您无法控制编译器将.Value
存储在临时匿名本地变量中。 - Mark Hurd