我有一个应用程序,它有一个数据树作为数据包。为了实现MVVM模式,我有一个封装数据树的类逻辑层。因此逻辑也被安排在树中。如果输入处于有效状态,则应将数据复制到第二个线程,该线程用作上一个有效状态的双缓冲区。因此,一种方法是克隆。
另一种方法是将完整的数据后端实现为不可变的。如果输入新内容,这意味着重建整个数据树。我的问题是,有一种实用的方法吗?我陷入了必须将数据树有效地重新分配给逻辑层的地步。
**更新 - 部分代码
我们正在做的是抽象我们用来运行实验的硬件设备。因此我们定义了类似“chassis,sequence,card,channel,step”的类。那些构建这样的树结构:
Chassis
/ \
Sequence1 Sequence2
/ | \
Card1 Card2 Card3
/ \
Channel1 Channel2
/ \
Step1 Step2
在代码中它看起来像这样:
public class Chassis{
readonly var List<Sequence> Sequences = new List<Sequence>();
}
public class Sequence{
readonly var List<Card> Cards = new List<Card>();
}
等等。当然每个类都有一些属性,但这些属性很容易处理。我现在的问题是List是一个可变对象。我可以调用List.Add()并改变它。好的是有一个ReadOnlyList,但我不确定它是否以正确的方式实现了不变性。正如复制值而不是引用而不仅仅是通过阻止set方法来阻止写入它。 下一个问题是序列和步骤的数量可以变化。出于这个原因,我需要列表元素的原子交换。 目前我还没有更多的代码,因为我仍在考虑这种方式是否有助于我,以及是否有可能在合理的时间内实现它。
答案 0 :(得分:4)
请注意,有新的immutable collections for .NET可以帮助您实现目标。
对Dave Turvey的陈述非常谨慎(如果可以的话,我会downvote / comment):
如果您要实现不可变列表,可以尝试将列表存储为私有成员,但公开公开
IEnumerable<>
这是不正确的。私有成员仍然可以通过其容器类进行更改。公共成员可以投放到List<T>
,IList<T>
或ICollection<T>
而不会抛出异常。因此,任何取决于公众IEnumerable<T>
不变性的事情都可能会破裂。
答案 1 :(得分:0)
我不确定我是否100%理解你的要求。听起来你有一个特定状态的对象树,你想在不修改原始对象状态的情况下对其副本执行某些处理。您应该通过&#34; Deep Copy&#34;来研究克隆。 This question应该让你开始。
如果您要实现不可变列表,可以尝试将列表存储为私有成员,但公开公开IEnumerable<>
public class Chassis
{
List<Sequence> _sequences = new List<Sequence>();
public IEnumerable<Sequence> Sequences { get { return _sequences; } }
}
18/04/13更新以回应Brandon Bonds的评论
在Brandon Bonds答案中链接的图书馆肯定很有趣,并提供优于IEnumerable<>
的优势。在许多情况下,它可能是更好的解决方案。但是,如果您使用此库,则应注意一些注意事项。
截至18/04/2013这是一个测试版库。它显然仍在开发中,可能还没有为生产使用做好准备。例如,链接文章中用于创建列表的代码示例在当前的nuget包中不起作用。
这是一个.net 4.5库。所以它不适合针对旧框架的程序。
它不保证集合本身仅包含在集合中的对象的不变性。可以修改不可变列表中的对象。您仍然需要考虑使用深层副本来复制集合。
这在本文末尾的常见问题解答中得到解决
问:我是否只能将不可变数据存储在这些不可变集合中?答:您可以在这些集合中存储所有类型的数据。唯一不可变的方面是集合本身,而不是它们包含的项目。
此外,以下代码示例说明了这一点(使用版本1.0.8-beta)
class Data
{
public int Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var test = ImmutableList.Create<Data>();
test = test.Add(new Data { Value = 1 });
Console.WriteLine(test[0].Value);
test[0].Value = 2;
Console.WriteLine(test[0].Value);
Console.ReadKey();
}
}
此代码将允许修改Data对象和输出
1
2
以下是一些有关此主题的进一步阅读的文章