Есть ли разумный способ выразить концепцию линейного типа в .Net(Compact Framework/desktop 3.5 common subset), в таком способ: (a) требуемый синтаксис не становится чрезмерно многословным, запутанным или иным образом болезненным; и (б) инвариант может быть либо принудительно применен во время выполнения, либо проверен анализом кода во время компиляции (поэтому программист по обслуживанию во всем - спешить спешить не может просто беспечно игнорировать инвариант)? Идея здесь заключается в том, чтобы избежать необходимости защитного копирования объектов команд на границах подсистемы.
Как представить линейные типы в С#/. Net?
Ответ 1
Существует два типа типов в .Net: типы ссылок и типы значений.
Когда вы копируете ссылочный тип, назначая его другой переменной, копируется только ссылка.
Когда вы копируете тип значения, копируется весь контент типа, байтом по байту.
В обоих случаях невозможно предотвратить, изменить или получить уведомление об этом (в отличие от конструкторов копирования С++). Это означает, что вы не можете реализовать линейные типы в .Net.
Вместо этого вы можете использовать неизменяемый (или зависающий) тип, как и другие.
Ответ 2
Линейные типы, основанные на теории линейной логики и тесно связанные с типами уникальности, являются типами, присваиваемыми значениям, имеющим свойство, что они имеют одну и только одну ссылку на них в любое время. Они полезны для описания больших неизменяемых значений, таких как файлы, строки и т.д.
Неизменяемым типом является тот, чье внутреннее состояние не может измениться после его создания.
A "глубоко неизменный тип" - это тот, чей график зависимости содержит ссылочные типы, которые также "глубоко неизменяемы". Если зависимые ссылочные типы сами по себе не являются "глубоко неизменными", тип называется "неглубоким неизменным".
В С# мы работаем со ссылочными типами и типами значений. Экземпляры ссылочных типов могут быть разделены между разрозненным одновременно исполняемым кодом, тогда как типы значений привязаны к стеку (если не помечены в бокс), скопированы при совместном использовании и, следовательно, автономны, хотя и не являются неизменяемыми (и могут содержать зависимости от других ссылочных типов, которые затем "копируются совместно" ).
Хотя способность делиться ссылочным типом, несомненно, является мощной особенностью объектно-ориентированных структур, в мире развития предприятия она также должна считаться одной из ее основных недостатков и использоваться с особой осторожностью. Все, что не может быть выполнено атомарно, демонстрирует хрупкость и возможность чередования чередующихся ошибок с перерывами в хаосе.
В С# лучшее, что мы можем сделать, это описать наши намерения. Неизменяемость может быть частично реализована путем маркировки всех внутренних состояний типа как частных, так и только для чтения. Глубокая неизменность не может быть применена (и в этом случае не может быть мелкой), поэтому разработчик должен придерживаться намерений. Изменения состояния происходят через статические методы, которые возвращают новые экземпляры типа, содержащего запрашиваемое состояние.
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);
}
}
Ответ 3
Предоставленная ссылка действительно определяет 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
, а затем поймать во время выполнения, когда его значение установлено и не используется.