Kit*_*Kit 30 .net c# mutable immutability
由于尽管在CLR中有一些支持,但不变性并没有完全融入C#到F#的程度,或完全进入框架(BCL),对于C#的(im)可变性有什么相当完整的解决方案?
我的偏好顺序是一个由兼容的一般模式/原则组成的解决方案
那
我还希望包含您作为社区可能提出的模式,这些模式不完全适合框架,例如通过接口表达可变性意图(其中两个客户端不应该更改某些内容并且可能只想更改某些内容通过接口这样做,而不是支持类(是的,我知道这不是真正的不变性,但足够):
public interface IX
{
int Y{ get; }
ReadOnlyCollection<string> Z { get; }
IMutableX Clone();
}
public interface IMutableX: IX
{
new int Y{ get; set; }
new ICollection<string> Z{ get; } // or IList<string>
}
// generally no one should get ahold of an X directly
internal class X: IMutableX
{
public int Y{ get; set; }
ICollection<string> IMutableX.Z { get { return z; } }
public ReadOnlyCollection<string> Z
{
get { return new ReadOnlyCollection<string>(z); }
}
public IMutableX Clone()
{
var c = MemberwiseClone();
c.z = new List<string>(z);
return c;
}
private IList<string> z = new List<string>();
}
// ...
public void ContriveExample(IX x)
{
if (x.Y != 3 || x.Z.Count < 10) return;
var c= x.Clone();
c.Y++;
c.Z.Clear();
c.Z.Add("Bye, off to another thread");
// ...
}
Run Code Online (Sandbox Code Playgroud)
更好的解决方案是使用F#,你想要真正的不变性吗?
就我个人而言,我并不真正了解此问题的任何第三方或以前的解决方案,因此如果我涉及旧问题,我深表歉意。但是,如果我要为我正在从事的项目实施某种不变性标准,我会从这样的开始:
\n\npublic interface ISnaphot<T>\n{\n T TakeSnapshot();\n}\n\npublic class Immutable<T> where T : ISnaphot<T>\n{\n private readonly T _item;\n public T Copy { get { return _item.TakeSnapshot(); } }\n\n public Immutable(T item)\n {\n _item = item.TakeSnapshot();\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n该接口的实现类似于:
\n\npublic class Customer : ISnaphot<Customer>\n{\n public string Name { get; set; }\n private List<string> _creditCardNumbers = new List<string>();\n public List<string> CreditCardNumbers { get { return _creditCardNumbers; } set { _creditCardNumbers = value; } }\n\n public Customer TakeSnapshot()\n {\n return new Customer() { Name = this.Name, CreditCardNumbers = new List<string>(this.CreditCardNumbers) };\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n客户端代码将类似于:
\n\n public void Example()\n {\n var myCustomer = new Customer() { Name = "Erik";}\n var myImmutableCustomer = new Immutable<Customer>(myCustomer);\n myCustomer.Name = null;\n myCustomer.CreditCardNumbers = null;\n\n //These guys do not throw exceptions\n Console.WriteLine(myImmutableCustomer.Copy.Name.Length);\n Console.WriteLine("Credit card count: " + myImmutableCustomer.Copy.CreditCardNumbers.Count);\n }\n
Run Code Online (Sandbox Code Playgroud)\n\n明显的缺陷是,实现的好坏取决于 的ISnapshot
实现的客户端TakeSnapshot
,但至少它会标准化事物,并且如果您遇到与可疑的可变性相关的问题,您会知道去哪里搜索。潜在的实现者也有责任认识到他们是否可以提供快照不变性并且不实现该接口,如果不能(即该类返回对不支持任何类型的克隆/复制的字段的引用,因此不能快照版)。
正如我所说,这是一个开始\xe2\x80\x94我可能如何开始\xe2\x80\x94当然不是一个最佳解决方案或一个完成的、完美的想法。从这里,我会看到我的用法是如何演变的,并相应地修改这种方法。但是,至少在这里我知道我可以定义如何使某些东西不可变并编写单元测试以确保它是不可变的。
\n\n我意识到这与实现对象复制相差不远,但它标准化了复制与不可变性。在代码库中,您可能会看到 的一些实现者ICloneable
、一些复制构造函数和一些显式复制方法,甚至可能在同一个类中。定义这样的东西告诉你,意图与不变性具体相关\xe2\x80\x94我想要一个快照而不是一个重复的对象,因为我碰巧想要n个以上的该对象。该类Immtuable<T>
还集中了不变性和副本之间的关系;如果您稍后想要以某种方式进行优化,例如缓存快照直到变脏,则无需在复制逻辑的所有实现器中执行此操作。