关于c#:LINQ选择多个 – 修改拼合的集合

LINQ Select Many - Modify flattened collection

我有一个名为Activity的类。此类包含圈数列表,并且每个圈数都有一组跟踪点。

活动—多个->圈数—多个->跟踪点。

每当我展开TracPoints集合时,都会得到所有TracPoints的列表。但是,当我修改那些原件时,它们当然不会被修改,因为它是一个副本。

在每圈的跟踪点列表中,是否有任何方法可以更改我对扁平跟踪点所做的任何更改?


只要tracpoint是一个结构,就不可能以任何合理的方式。

每当将结构变量或字段的值赋给另一个变量或字段时,都会复制其内容。同样,当它作为方法参数传递或从方法返回时,它的值会被复制。这是值语义[1]。将其与具有值语义的原子类型(如int)进行比较。您可能希望下面的代码打印2,而不是3。

1
2
3
4
5
6
static function Change(int j) { j = 3; }
static void Main(string[] args) {
   int i = 2;
   Change(i);
   System.Console.WriteLine(i);
}

如果执行selectmany,则集合中的每个值都可能被分配给某个临时局部变量,然后从迭代器(selectmany)返回,因此,在迭代器中输出值之前,它会被复制,实际上可能会被复制多次。所以您要更新的是结构的副本。像在示例中一样,您没有更改变量i,而是将其副本存储在变量j中。

这就是为什么结构应该是不可变的。不要在结构中具有getter和setter属性,它们应该只有getter。要更改结构的属性值,可以实现复制整个原始结构的方法、更改所需属性的值并返回新的结构实例。事实上,它的副本也会被退回。例子:

1
2
3
4
5
6
7
8
9
10
11
12
struct S {
   int f;
   public int F { get { return this.f; } }
   public S SetF(int newVal) {
      var s = new S();
      s.f = newVal;
      return s;
   }
}

var x = new S();
x = x.SetF(30);

也就是说,可以用指针和不安全的C来实现您想要的,但是相信我,将您的结构改为类会更容易,这样它们就具有引用语义而不是值语义,或者保留它们的结构,但使它们不可变,不使用LINQ,而是使用旧的循环。如果您想在这种场景中对selectmany之类的东西使用linq,那么您可能不太关心结构和类之间的性能差异…

[1]http://msdn.microsoft.com/en-us/library/aa664472(v=vs.71).aspx