C# 相互引用的不可变对象?
今天我试着把我的头绕在相互参照的不可变对象上。我得出的结论是,如果不使用惰性求值,就不可能做到这一点,但在这个过程中,我编写了这段(我认为)有趣的代码C# 相互引用的不可变对象?,c#,immutability,C#,Immutability,今天我试着把我的头绕在相互参照的不可变对象上。我得出的结论是,如果不使用惰性求值,就不可能做到这一点,但在这个过程中,我编写了这段(我认为)有趣的代码 public class A { public string Name { get; private set; } public B B { get; private set; } public A() { B = new B(this); Name = "test"; }
public class A
{
public string Name { get; private set; }
public B B { get; private set; }
public A()
{
B = new B(this);
Name = "test";
}
}
public class B
{
public A A { get; private set; }
public B(A a)
{
//a.Name is null
A = a;
}
}
我发现有趣的是,我想不出另一种方法来观察A型对象处于一种尚未完全构造且包含线程的状态。为什么这是正确的?是否有其他方法来观察一个未完全构造的对象的状态? 如果您考虑初始化顺序
- 导出静态场
- 派生静态构造函数
- 派生实例字段
- 基本静态场
- 基本静态构造函数
- 基本实例字段
- 基本实例构造函数
- 派生实例构造函数
显然,通过向上转换,您可以在调用派生实例构造函数之前访问该类(这就是您不应该使用构造函数中的虚拟方法的原因。它们可以轻松访问未由构造函数初始化的派生字段/派生类中的构造函数不能使派生类处于“一致”状态state)原则是不要让您的这个对象从构造函数主体中逃逸
观察此类问题的另一种方法是在构造函数中调用虚方法。可以通过在构造函数中最后实例化B来避免问题:
public A()
{
Name = "test";
B = new B(this);
}
如果你的建议不可能,那么A也不是一成不变的
编辑:由于leppie,修复了此问题。完全构造”是由您的代码定义的,而不是由语言定义的
这是从构造函数调用虚拟方法的变体,总的方针是:不要那样做
要正确实现“完全构造”的概念,请不要将
此
从构造函数中传递出去 实际上,在构造函数期间泄漏此
引用将允许您这样做;显然,如果在不完整的对象上调用方法,可能会导致问题。至于“观察未完全构造对象状态的其他方法”:
- 在构造函数中调用
方法;子类构造函数还没有被调用,因此virtual
可能会尝试访问未完成状态(在子类中声明或初始化的字段等)重写
- 反射,可能使用
(它创建对象时根本不调用构造函数)FormatterServices.GetUninitializedObject
为什么你认为它是无效的 因为构造函数应该保证它包含的代码在外部代码能够观察对象的状态之前被执行 对。但编译器不负责维护该不变量。你是。如果你写的代码破坏了这个不变量,当你这么做的时候,它会伤害你,那么停止这样做 有没有其他方法可以观察未完全构造的对象的状态 当然。对于引用类型,所有这些类型都涉及以某种方式从构造函数中传递“this”,显然,因为保存对存储的引用的唯一用户代码是构造函数。构造函数泄漏“this”的一些方法是:
- 将“this”放在一个静态字段中,并从另一个线程引用它
- 进行方法调用或构造函数调用,并将“this”作为参数传递
- 进行虚拟调用——如果虚拟方法被派生类重写,则尤其令人讨厌,因为它会在派生类主体运行之前运行
A = new Node("A");
B = new Node("B");
G = Graph.Empty.AddNode(A).AddNode(B).AddEdge(A, B).AddEdge(B, A);