【问题标题】:LINQ - Does the Where expression return new instance or reference to object instanceLINQ - Where 表达式是否返回新实例或对对象实例的引用
【发布时间】:2010-08-27 12:38:58
【问题描述】:

这对某些人来说可能是一个基本问题,但它会影响我如何设计我的程序。

我有一个 A 类型的集合:

IEnumerable<A> myCollection;

我正在根据 2 个不同的标准过滤我的收藏:

IEnumerable<A> subCollection1 = myCollection.Where(x => x.Count > 10);
etc.

现在,我知道 .Where 表达式将返回 IEnumerable 的新实例,但新集合是否包含对“myCollection”引用的 A 类型实例的相同引用,或者是否创建了 A 类型的新副本?如果创建了“A”类型的新实例,有没有办法说“subCollection1”引用了与“myCollection”引用相同的 A 实例?

编辑:添加进一步说明。

我正在寻找一种方法,以便当我对“subCollection1”中的“A”实例进行更改时,它也会针对“myCollection”进行修改。

【问题讨论】:

    标签: c# .net linq


    【解决方案1】:

    如果它们是类,则实例相同,但如果它们是结构/值类型,则复制。

    int、byte 和 double 是 value types,结构体(如 System.Drawing.Point 和自定义结构体)也是如此。 但是字符串,所有你自己的类,基本上是“其余的”,都是reference types

    注意:LINQ 使用与所有其他分配相同的规则。

    对于对象:

    Person p1 = new Person();
    p1.Name = "Mr Jones";
    Person p2 = p1;
    p2.Name = "Mr Anderssen";
    // Now p1.Name is also "Mr Anderssen"
    

    对于结构:

    Point p1 = new Point();
    p1.x = 5;
    Point p2 = p1;
    p2.x = 10;
    // p1.x is still 5
    

    使用 LINQ 时适用相同的规则。

    【讨论】:

    • 这就是我的假设。我只是想确保 Where 表达式没有对我的类实例进行某种浅拷贝。
    • 值得注意的是匿名类型总是类,所以引用也是如此。
    • @Richard:另外值得注意的是,匿名类型在 C# 中是不可变的,因此无论如何您都不能修改匿名类型实例。
    • 这个答案准确吗?就像 romanlv 和 Andrey 所说,一些 linq 方法确实返回一个新集合而不是引用。
    【解决方案2】:

    实际上这取决于集合。在某些情况下,LINQ 方法可以返回克隆对象而不是对原始对象的引用。看看这个测试:

    [Test]
    public void Test_weird_linq()
    {
        var names = new[]{ "Fred", "Roman" };
        var list = names.Select(x => new MyClass() { Name = x });
    
        list.First().Name = "Craig";
        Assert.AreEqual("Craig", list.First().Name);            
    }
    
    public class MyClass
    {
        public string Name { get; set; }
    }
    

    即使很多人认为list.First() 会返回相同的对象,这个测试也会失败。如果您使用另一个“使用ToList() 修改”的集合,它将起作用。

    var list = names.Select(x => new MyClass() { Name = x }).ToList();
    

    我不确定它为什么会这样工作,但在编写代码时需要牢记这一点 :)

    This question 可以帮助您了解 LINQ 内部的工作原理。

    【讨论】:

      【解决方案3】:

      它们是相同的对象。 Where 仅过滤器,Select 产生(可以产生)新实例。

      【讨论】:

        【解决方案4】:

        创建一个引用类型的新对象并非易事。 LINQ 不知道该怎么做。 LINQ 在处理引用类型时总是返回相同的实例。

        【讨论】:

          【解决方案5】:

          我只是想补充一些其他答案——一般来说,当我不确定某事但需要特定行为时,我会为它添加一个单元测试。您可以轻松地将其放入测试中,然后检查是否相等,这将告诉您是否正在查看原始容器中对象的引用。有些人可能会说这很愚蠢,因为你“应该知道”会发生什么,但对我来说,我知道我要么 1)不确定,因为我不是一个了不起的程序员,2)总有晚上我不得不燃烧午夜的油,让你确信某件事会按照你的需要行事是件好事。

          【讨论】:

          • 之所以提出这个问题,是因为当我有一个使用 Where 表达式过滤的集合时,我的实体框架自我跟踪实体的行为不正确。我相信这个问题不是因为我对 C# 如何处理引用的误解,而是在执行 Where 表达式后跟踪被关闭的问题。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多