我可以称这个C#类为“不可变”吗?

时间:2014-10-31 15:05:16

标签: c# immutability mutable

我需要将我的可变不可变,现在看起来如下所示。但是,我仍然不确定我是否有一个完全"不可变的*类,如果是,那么这个叫做 immutability 的是什么?

public class B<C, M>
        where C : IComparable<C>
        where M : IMetaData
{

    internal B(char tau, M metadata, B<C, M> nextBlock)
    {
        if (tau == 'R') omega = 1;
        _lambda = new List<Lambda<C, M>>();
        _lambda.Add(new Lambda<C, M>(tau: tau, atI: metadata));
        foreach (var item in nextBlock.lambda)
            if (item.tau != 'L')
                _lambda.Add(new Lambda<C, M>(tau: 'M', atI: item.atI));
    }

    internal int omega { private set; get; }
    private List<Lambda<C, M>> _lambda { set; get; }
    internal ReadOnlyCollection<Lambda<C, M>> lambda { get { return _lambda.AsReadOnly(); } }



    internal B<C, M> Update(int Omega, char tau, M metadata)
    {
        B<C, M> newBlock = new B<C, M>();
        newBlock.omega = Omega;
        newBlock._lambda = new List<Lambda<C, M>>(this._lambda);
        newBlock._lambda.Add(new Lambda<C, M>(tau: tau, atI: metadata));
        return newBlock;
    }

    internal B<C, M> Update(Dictionary<uint, Lambda<C, M>> lambdas)
    {
        B<C, M> newBlock = new B<C, M>();
        newBlock.omega = this.omega;
        newBlock._lambda = new List<Lambda<C, M>>();
        foreach (var l in lambdas)
            newBlock._lambda.Add(new Lambda<C, M>(tau: l.Value.tau, atI: l.Value.atI));

        return newBlock;
    }
}

public class Lambda<C, M>
        where C : IComparable<C>
        where M : IMetaData
{
    internal Lambda(char tau, M atI)
    {
        this.tau = tau;
        this.atI = atI;
    }

    internal char tau { private set; get; }
    internal M atI { private set; get; }
}

根据我的申请B需要不时更改;因此,为了保持 immutability 的属性,每次更新都需要通过Update函数来完成,该函数返回一个全新的B

更新

要了解 Jon Skeet 巧妙发现的IMetaData,请考虑以下定义:

public interface IMetaData
    {
        UInt32 hashKey { set; get; }
    }

并将以下类作为M传递给B<C, M>

public class MetaData : IMetaData
    {
        public UInt32 hashKey { set; get; }
    }

2 个答案:

答案 0 :(得分:5)

没有任何外部代码可以观察到这种类型的任何突变,这足以让它被认为是不可变的&#34;共同的演讲。它曾经变异的唯一时间是在它自己的构造函数中;它一旦创建就永远不会变异,所以没有任何外部实体能够真正观察到类型的变异。对于几乎所有被认为是“不可变”的类型而言,这种情况往往都是正确的。

虽然类型确实具有在其构造函数之外变异的技术能力,因为它具有非readonly和可变List的字段,但它实际上从不执行任何此类突变或暴露任何改变数据的方法。

答案 1 :(得分:2)

绝大多数不可变类类型通过将数据封装在可变类类型的对象中来实现其不变性,但确保不会将对该对象的引用暴露给可能使其变异的代码。鉴于.NET没有除String以外的任何不可变数组类型,这种方法通常是可变大小集合提供有效随机访问的唯一实用方法。不需要提供有效随机访问的类型可以使用更多“深度不可变”的方法来存储数据,例如链接列表或存储readonly字段中所有内容的树,但实际上如果没有可能的执行顺序,一个特定的对象实例可以被改变,然后该特定实例可以被合法地描述为“不可变的”,即使它的类允许实例被任何引用它的人变异。

关于接口类型引用是否可以被视为标识不可变对象,这将取决于接口本身或提供引用的方式中是否存在合同,这将指定或者该接口的所有合法实现都是不可变的,由引用标识的特定实例将是不可变的,或者(如果持有引用的代码永远不会将它暴露给外部代码)该实例永远不会暴露给可能的代码改变它。如果接口承诺不变,那么从外部代码接收引用的代码可以直接使用它;否则,它必须从代码中接收引用,该代码承诺不会对其进行任何“未经过保护的”引用。一种可能的模式是让接口提供一个AsReadableSnaphot方法,该方法可以保证在调用它时返回一个封装对象状态的对象,并保证不会存在任何外部引用,这会改变对象的状态。题;一个不可变的类可能会实现这样一个方法来简单地返回自己,而一个可变的类可能实现它来返回它自己的克隆)。如果类的实例可能很大,则可能需要AsReadableSnapshot创建一个不可变的类实例(这样调用AsReadableSnapshot就不必再生成另一个数据副本了)但是如果它们总是很小,返回的对象属于可变类型应该不是问题。