“a”是什么;“基本完成”;C#的(im)可变性方法?
既然C#中的不变性没有完全融入F#的程度,或者尽管CLR中有一些支持,但它也没有完全融入框架(BCL),那么C#的(im)可变性的完全解决方案是什么呢 我的优先顺序是一个解决方案,包括与“a”是什么;“基本完成”;C#的(im)可变性方法?,c#,.net,immutability,mutable,C#,.net,Immutability,Mutable,既然C#中的不变性没有完全融入F#的程度,或者尽管CLR中有一些支持,但它也没有完全融入框架(BCL),那么C#的(im)可变性的完全解决方案是什么呢 我的优先顺序是一个解决方案,包括与 具有很少依赖项的单个开源库 少量补充/兼容的开源库 商业的东西 那 涵盖了Lippert的各种 提供良好的性能(我知道这很模糊) 支持序列化 支持克隆/复制(深/浅/部分?) 在DDD、构建器模式、配置和线程等场景中感觉自然 提供不可变的集合 我还希望包括社区可能会提出的模式,这些模式不完全适合框架,例
- 具有很少依赖项的单个开源库
- 少量补充/兼容的开源库
- 商业的东西
- 涵盖了Lippert的各种
- 提供良好的性能(我知道这很模糊)
- 支持序列化
- 支持克隆/复制(深/浅/部分?)
- 在DDD、构建器模式、配置和线程等场景中感觉自然
- 提供不可变的集合
公共接口IX
{
int Y{get;}
只读集合Z{get;}
IMutableX克隆();
}
公共接口IMutableX:IX
{
新int Y{get;set;}
新的ICollection Z{get;}//或IList
}
//一般来说,没有人应该直接得到一个X
内部类X:IMutableX
{
公共整数Y{get;set;}
ICollection IMutableX.Z{get{return Z;}}
公共只读集合Z
{
获取{返回新的只读集合(z);}
}
公共IMutableX克隆()
{
var c=MemberwiseClone();
c、 z=新列表(z);
返回c;
}
private IList z=新列表();
}
// ...
公共无效供款示例(IX x)
{
如果(x.Y!=3 | | x.Z.计数<10)返回;
var c=x.Clone();
c、 Y++;
c、 Z.清除();
c、 Z.添加(“再见,转到另一个线程”);
// ...
}
就我个人而言,我并不知道有任何第三方或以前的解决方案可以解决这个问题,因此,如果我涉及到老问题,我深表歉意。但是,如果我要为我正在进行的项目实施某种不变性标准,我会从以下内容开始:
public interface ISnaphot<T>
{
T TakeSnapshot();
}
public class Immutable<T> where T : ISnaphot<T>
{
private readonly T _item;
public T Copy { get { return _item.TakeSnapshot(); } }
public Immutable(T item)
{
_item = item.TakeSnapshot();
}
}
公共接口是naphot
{
T TakeSnapshot();
}
公共类不可变,其中T:ISnaphot
{
专用只读T_项;
公共T复制{get{return _item.TakeSnapshot();}
公共不可变项(T项)
{
_item=item.TakeSnapshot();
}
}
该接口的实现方式如下:
public class Customer : ISnaphot<Customer>
{
public string Name { get; set; }
private List<string> _creditCardNumbers = new List<string>();
public List<string> CreditCardNumbers { get { return _creditCardNumbers; } set { _creditCardNumbers = value; } }
public Customer TakeSnapshot()
{
return new Customer() { Name = this.Name, CreditCardNumbers = new List<string>(this.CreditCardNumbers) };
}
}
公共类客户:ISnaphot
{
公共字符串名称{get;set;}
私有列表_creditCardNumbers=新列表();
公共列表CreditCardNumbers{get{return}CreditCardNumbers;}set{CreditCardNumbers=value;}
公共客户快照()
{
return new Customer(){Name=this.Name,CreditCardNumbers=new List(this.CreditCardNumbers)};
}
}
客户机代码类似于:
public void Example()
{
var myCustomer = new Customer() { Name = "Erik";}
var myImmutableCustomer = new Immutable<Customer>(myCustomer);
myCustomer.Name = null;
myCustomer.CreditCardNumbers = null;
//These guys do not throw exceptions
Console.WriteLine(myImmutableCustomer.Copy.Name.Length);
Console.WriteLine("Credit card count: " + myImmutableCustomer.Copy.CreditCardNumbers.Count);
}
public void示例()
{
var myCustomer=new Customer(){Name=“Erik”;}
var myImmutableCustomer=新的不可变(myCustomer);
myCustomer.Name=null;
myCustomer.CreditCardNumber=null;
//这些人不会抛出异常
Console.WriteLine(myImmutableCustomer.Copy.Name.Length);
Console.WriteLine(“信用卡计数:+myImmutableCustomer.Copy.CreditCardNumber.count”);
}
最明显的缺陷是,该实现只与ISnapshot
的TakeSnapshot
实现的客户端一样好,但至少它会使事情标准化,并且如果您有与可疑的可变性相关的问题,您会知道去哪里搜索。潜在的实现者也要重新编码确定它们是否可以提供快照不可变性,如果不能,则不实现接口(即,该类返回对不支持任何类型克隆/复制的字段的引用,因此无法创建快照)
正如我所说的,这是一个开始,我可能不会开始一个最佳的解决方案或一个完善的想法。从这里,我会看到我的使用是如何演变的,并相应地修改这个方法。但是,至少在这里,我知道我可以定义如何使某些东西不可变,并编写单元测试来确保它是不变的
我意识到,这与仅仅实现对象副本并不遥远,但它标准化了副本与vis不变性。在代码库中,您可能会看到一些
ICloneable
的实现者、一些副本构造函数和一些显式复制方法,甚至可能是在同一个类中。定义类似的内容可以告诉您ion特别与不变性相关——我想要一个快照,而不是一个重复的对象,因为我恰好想要多个该对象。immuable
类还集中了不变性和副本之间的关系;如果以后想以某种方式进行优化,比如将快照缓存到脏为止,则根本不需要这样做复制逻辑的实现者。更好的解决方案是在您想要真正不可变的地方使用F#吗?使用我提出的方法来解决这个问题。它通常应该适合您需要创建的任何类型的不可变对象的需要
不需要使用泛型或使用任何接口。出于我的目的,我不希望我的不可变类可以相互转换。你为什么要这样做?它们应该共享哪些共同特征,这意味着它们应该相互转换?强制执行代码模式应该是代码生成器的工作(或者更好的是,一个足够好的类型系统,允许您定义通用代码模式,不幸的是C#没有)
下面是模板的一些示例输出,以说明基本的con
public void Example()
{
var myCustomer = new Customer() { Name = "Erik";}
var myImmutableCustomer = new Immutable<Customer>(myCustomer);
myCustomer.Name = null;
myCustomer.CreditCardNumbers = null;
//These guys do not throw exceptions
Console.WriteLine(myImmutableCustomer.Copy.Name.Length);
Console.WriteLine("Credit card count: " + myImmutableCustomer.Copy.CreditCardNumbers.Count);
}
public sealed partial class CommitPartial
{
public CommitID ID { get; private set; }
public TreeID TreeID { get; private set; }
public string Committer { get; private set; }
public DateTimeOffset DateCommitted { get; private set; }
public string Message { get; private set; }
public CommitPartial(Builder b)
{
this.ID = b.ID;
this.TreeID = b.TreeID;
this.Committer = b.Committer;
this.DateCommitted = b.DateCommitted;
this.Message = b.Message;
}
public sealed class Builder
{
public CommitID ID { get; set; }
public TreeID TreeID { get; set; }
public string Committer { get; set; }
public DateTimeOffset DateCommitted { get; set; }
public string Message { get; set; }
public Builder() { }
public Builder(CommitPartial imm)
{
this.ID = imm.ID;
this.TreeID = imm.TreeID;
this.Committer = imm.Committer;
this.DateCommitted = imm.DateCommitted;
this.Message = imm.Message;
}
public Builder(
CommitID pID
,TreeID pTreeID
,string pCommitter
,DateTimeOffset pDateCommitted
,string pMessage
)
{
this.ID = pID;
this.TreeID = pTreeID;
this.Committer = pCommitter;
this.DateCommitted = pDateCommitted;
this.Message = pMessage;
}
}
public static implicit operator CommitPartial(Builder b)
{
return new CommitPartial(b);
}
}
CommitPartial cp = new CommitPartial.Builder() { Message = "Hello", OtherFields = value, ... };
CommitPartial.Builder cpb = new CommitPartial.Builder();
cpb.Message = "Hello";
...
// using the implicit conversion operator:
CommitPartial cp = cpb;
// alternatively, using an explicit cast to invoke the conversion operator:
CommitPartial cp = (CommitPartial)cpb;