问题很简单.可以改变其内部状态而不能从外部观察的类型是否可以被认为是不可变的?
简化示例:
public struct Matrix
{
bool determinantEvaluated;
double determinant;
public double Determinant
{
get //asume thread-safe correctness in implementation of the getter
{
if (!determinantEvaluated)
{
determinant = getDeterminant(this);
determinantEvaluated = true;
}
return determinant;
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新:澄清线程安全问题,因为它导致分心.
The*_*kis 33
这取决于.
如果您正在记录客户端代码的作者或作为客户端代码的作者进行推理,那么您关注组件的接口(即,其外部可观察状态和行为)而不关注其实现细节(如内部表示) ).
从这个意义上说,即使一个类型缓存状态,即使它是懒惰地初始化等,类型也是不可变的 - 只要这些突变在外部是不可观察的.换句话说,如果类型在通过其公共接口(或其他预期用例,如果有)使用时表现为不可变,则该类型是不可变的.
当然,要做到这一点可能很棘手(有了可变的内部状态,你可能需要关注线程安全,序列化/编组行为等).但假设你确实做到了(至少你需要的程度),没有理由不考虑这种类型是不可变的.
显然,从编译器或优化器的角度来看,这种类型通常不被认为是不可变的(除非编译器足够智能或具有一些"帮助",如某些类型的提示或先验知识)以及任何预期的优化对于不可变类型可能不适用,如果是这种情况.
Dmi*_*nko 17
是的,不可变的可以改变它的状态,只要软件的其他组件(通常是缓存)看不到变化 .很像量子物理学:事件应该让观察者成为一个事件.
在您的情况下,可能的实现是这样的:
public class Matrix {
...
private Lazy<Double> m_Determinant = new Lazy<Double>(() => {
return ... //TODO: Put actual implementation here
});
public Double Determinant {
get {
return m_Determinant.Value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,Lazy<Double> m_Determinant
具有变化的状态
m_Determinant.IsValueCreated
Run Code Online (Sandbox Code Playgroud)
然而,这是不可观察的.
如果一棵树落在树林里,它会发出声音吗?
如果纯函数改变一些本地数据以产生不可变的返回值,那可以吗?
为了性能原因,改变暴露API的对象是完全合理的,这些API对外部是不可变的.关于不可变对象的重要之处在于它们对外界的不变性.封装在其中的一切都是公平的游戏.
在像C#这样的垃圾收集语言中,由于GC,所有对象都具有某种状态.作为一个通常不应该关注你的消费者.
我会伸出脖子......
不,不可变对象不能在C#中更改其内部状态,因为观察其内存是一个选项,因此您可以观察到未初始化状态.证明:
public struct Matrix
{
private bool determinantEvaluated;
private double determinant;
public double Determinant
{
get
{
if (!determinantEvaluated)
{
determinant = 1.0;
determinantEvaluated = true;
}
return determinant;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后...
public class Example
{
public static void Main()
{
var unobserved = new Matrix();
var observed = new Matrix();
Console.WriteLine(observed.Determinant);
IntPtr unobservedPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof (Matrix)));
IntPtr observedPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Matrix)));
byte[] unobservedMemory = new byte[Marshal.SizeOf(typeof (Matrix))];
byte[] observedMemory = new byte[Marshal.SizeOf(typeof (Matrix))];
Marshal.StructureToPtr(unobserved, unobservedPtr, false);
Marshal.StructureToPtr(observed, observedPtr, false);
Marshal.Copy(unobservedPtr, unobservedMemory, 0, Marshal.SizeOf(typeof (Matrix)));
Marshal.Copy(observedPtr, observedMemory, 0, Marshal.SizeOf(typeof (Matrix)));
Marshal.FreeHGlobal(unobservedPtr);
Marshal.FreeHGlobal(observedPtr);
for (int i = 0; i < unobservedMemory.Length; i++)
{
if (unobservedMemory[i] != observedMemory[i])
{
Console.WriteLine("Not the same");
return;
}
}
Console.WriteLine("The same");
}
}
Run Code Online (Sandbox Code Playgroud)