我通常需要一个只读的 ObservableCollection (以便它的引用或内容只能在此类中修改)。我需要 ObservableCollection,因为我将使用 WPF 绑定到该属性(下面的代码中未显示)。这是一个完全可复制的工作示例,显示了我的意图:
public class MyClass
{
public ReadOnlyCollection<string> Values { get { return _valuesRO; } }
private readonly ObservableCollection<string> _values;
private readonly ReadOnlyObservableCollection<string> _valuesRO;
public MyClass()
{
_values = new();
_valuesRO = new(_values);
}
public void AddValue(string value)
{
_values.Add(value + ":");
}
}
public static class MyApp
{
public static void Main()
{
MyClass myClass = new();
myClass.AddValue("192"); //-> should work
Debug.Print(myClass.Values[0]); //-> should work
myClass.Values = new(); //-> should NOT work
myClass.Values.Add("192"); //-> should NOT work
}
}
我从这里得到了这个图案。
但是,这需要为每个公共集合编写大量代码 - 构造函数中需要两个附加字段和两行代码。如果我的类中需要很多这样的集合,它就会很快膨胀,从而损害代码的可读性。
鉴于我链接的解决方案已经有 14 年历史了,有什么方法可以用更少的代码实现相同的功能,最好没有额外的字段,并且构造函数中什么也没有?是否可以定义我自己的派生类来抽象此行为?理想的声明和使用应该是这样的:
public class MyClass
{
public MySpecialReadOnlyCollection<string> Values { get; init; } = new();
public void AddValue(string value)
{
Values.Add(value + ":");
}
}
public static class MyApp
{
public static void Main()
{
MyClass myClass = new();
myClass.AddValue("192"); //-> should work
Debug.Print(myClass.Values[0]); //-> should work
myClass.Values = new(); //-> should NOT work
myClass.Values.Add("192"); //-> should NOT work
}
}
如果不可能实现这一目标,我可以在这里做的下一个最好的事情是什么?
您想做这样的事情,按文档顺序初始化您的成员:
public class MyClass
{
private ObservableCollection<string> _values { get; } = new();
public ReadOnlyCollection<string> Values { get; } = new(_values);
但是你不能,因为你会得到编译错误
A field initializer cannot reference the non-static field, method, or property 'MyClass._values'
此错误在 编译器错误 CS0236 中进行了解释:
实例字段不能用于初始化方法之外的其他实例字段
相反,您可以使用字段来初始化构造函数内的其他字段。 您没有提及您正在使用的 C# 版本,但由于您使用的是目标类型 new()
,您正在使用
C# 9.0或更高版本,并且可以执行以下操作:
public class MyClass
{
private readonly ObservableCollection<string> _values; // Initialize in the constructor
public ReadOnlyCollection<string> Values { get; } // Initialize in the constructor
public MyClass() => this.Values = new(_values = new());
通过使用 lambda 语法,这只需要比未编译的所需最小语法多一行。
演示
这里 在 .NET Framework 中,某些语法糖不可用。 那时我们需要做:public class MyClass
{
private readonly ObservableCollection<string> _values;
public ReadOnlyCollection<string> Values { get; private set; }
public MyClass()
{
this.Values = new ReadOnlyCollection<string>(_values = new ObservableCollection<string>());
}
演示小提琴 #2这里