我对不可变对象进行了以下尝试:
class MyObject
{
private static int nextId;
public MyObject()
{
_id = ++nextId;
}
private int _id;
public int Id { get { return _id; } }
public string Name { get; private set; }
}
然后,我尝试使用它:
MyObject o1 = new MyObject { Name = "foo" };
但是对象初始化器失败了,因为Name
的setter是私有的。有没有办法解决这个问题,还是我必须在其中一个选择?
答案 0 :(得分:26)
您不能将对象初始值设定项与不可变对象一起使用。它们需要可设置的属性。
不可变对象意味着“创建后不会改变”。使Name
构造函数参数巧妙地表达了这一原则。
如果对象对于可理解的构造函数来说太复杂,您还可以使用Builder模式。通常,构建器本身将具有可变属性(可以在对象初始化器中使用),并且其.Build()方法将创建实际实例。
编辑(OP):我将添加我自己在这里制作的构建器的示例,然后接受这个答案,因为它提出了合理的解决方案。
class MyObject
{
public class Builder
{
public Builder()
{
// set default values
Name = String.Empty;
}
public MyObject Build()
{
return new MyObject(Name);
}
public string Name { get; set; }
}
private static int nextId;
protected MyObject(string name)
{
Id = ++nextId;
Name = name;
}
public int Id { get; private set; }
public string Name { get; private set; }
}
然后,您可以使用以下内容构建它的实例:
MyObject test = new MyObject.Builder { Name = "foo" }.Build();
编辑:这是我对模式的看法:
public abstract class Builder<T>
{
public static implicit operator T(Builder<T> builder)
{
return builder.Build();
}
private bool _built;
public T Build()
{
if(_built)
{
throw new InvalidOperationException("Instance already built");
}
_built = true;
return GetInstance();
}
protected abstract T GetInstance();
}
以下是使用Builder<T>
实施的示例。它利用嵌套类型的作用域规则来访问私有setter:
public class MyObject
{
private static int nextId;
protected MyObject()
{
Id = ++nextId;
}
public int Id { get; private set; }
public string Name { get; private set; }
public sealed class Builder : Builder<MyObject>
{
private MyObject _instance = new MyObject();
protected override MyObject GetInstance()
{
// Validate properties here
return _instance;
}
public string Name
{
get { return _instance.Name; }
set { _instance.Name = value; }
}
}
}
它隐式转换为目标类型,允许您执行此操作:
MyObject myObject = new MyObject.Builder { Name = "Some name" };
或者这个:
public void Foo(MyObject myObject)
// ...
Foo(new MyObject.Builder { Name = "Some name" });
答案 1 :(得分:8)
您需要在构造函数中设置属性,并且您不需要为id添加单独的局部变量:
class MyObject {
private static int nextId = 0;
public MyObject(string name) {
Id = ++nextId;
Name = name;
}
public int Id { get; private set; }
public string Name { get; private set; }
}
创建:
MyObject o1 = new MyObject("foo");
答案 2 :(得分:4)
将setter设为public或创建构造函数重载以设置Property Name 。
class MyObject{
public MyObject(string name) {
Name = name;
}
public string Name { get; private set; }
}
电话
MyObject o1 = new MyObject { Name = "foo" };
相当于
MyObject o1 = new MyObject();
o1.Name = "foo"; //This doesn´t work, cause the setter is private.
要使其真正不可变,请为属性 Name 创建一个字段,并使其成为只读状态。所以Property Name 只能使用构造函数设置,并且在运行时不能更改。
class MyObject{
private readonly string _name;
public MyObject(string name) {
_name = name;
}
public string Name {
get { return _name; }
}
}
答案 3 :(得分:3)
您不能使用对象初始值设定项并且具有不可变对象,因为对象初始值设定项要求属性设置器是公共的。公共制定者意味着它不会是不可变的。
唯一的方法就是模拟不变性,方法是在属性设置器中调用异常后再调用一次。
我个人认为这不是一个好的设计,我会质疑你为什么如此热衷于使用对象初始化器而不是构造器。语法几乎相同。