“a”是什么;“基本完成”;C#的(im)可变性方法?

“a”是什么;“基本完成”;C#的(im)可变性方法?,c#,.net,immutability,mutable,C#,.net,Immutability,Mutable,既然C#中的不变性没有完全融入F#的程度,或者尽管CLR中有一些支持,但它也没有完全融入框架(BCL),那么C#的(im)可变性的完全解决方案是什么呢 我的优先顺序是一个解决方案,包括与 具有很少依赖项的单个开源库 少量补充/兼容的开源库 商业的东西 那 涵盖了Lippert的各种 提供良好的性能(我知道这很模糊) 支持序列化 支持克隆/复制(深/浅/部分?) 在DDD、构建器模式、配置和线程等场景中感觉自然 提供不可变的集合 我还希望包括社区可能会提出的模式,这些模式不完全适合框架,例

既然C#中的不变性没有完全融入F#的程度,或者尽管CLR中有一些支持,但它也没有完全融入框架(BCL),那么C#的(im)可变性的完全解决方案是什么呢

我的优先顺序是一个解决方案,包括与

  • 具有很少依赖项的单个开源库
  • 少量补充/兼容的开源库
  • 商业的东西

  • 涵盖了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;