2009-07-27 80 views
0

我对LINQ和Lambda查询的结果有疑问。例如,我有以下代码:关于LINQ和Lambda查询的问题

class ClassA<T> { 
    public string Name { get; set; } 
    public T ObjectT { get; set; } 
} 

List<ClassA<T>> list; 
// list is populated 
// First way to get instance from list, reference type? 
ClassA<T> instance1 = list.Select(x=> x).Where(x=>x.Name == "A"). 
    FirstOrDefault(); 
// Second way to clone or copy instance from the list 
ClassA<T> instance2 = list.Select(x=> 
    new ClassA<T> { Name = x.Name, ObjectT = x.ObjectT}). 
    Where(x=> x.Name = "A").FirstOrDefault(); 

显然,instance2是在列表中找到的实例的克隆或副本。 instance1怎么样?这是一个新实例还是仅仅是对列表中实例的引用?如果instance1是对列表项的对象引用,则对其属性的任何更改都可能会更改列表中的同一对象。是对的吗?

如果是这种情况,我不想对列表中的对象有任何隐式的影响,我想我应该使用第二种策略。但是,如果我确实希望检索到的实例中的任何更改在列表中也有相同的更改,我应该使用策略1.不确定我的理解是否正确。任何意见?

回答

1

线由于您ClassA<T>扩展方法是class(不是struct),instance1是到作为list内的相同对象的引用。

关于策略,如果你不想让你列表的用户能够围绕列表元素一塌糊涂,我建议这些替代方案:

  • ClassA<T>一个struct。这样任何查询都会返回列表中元素的副本;

  • 使ClassA<T>实施IClonable像下面,克隆它,然后传递给一个不可信的代码。

请注意,ObjectT属性可能是一个类。因此,即使在克隆ClassA<T>对象后,ObjectT属性也将是对同一对象的引用,并且任何用户代码都可以对其进行修改。也许,你也需要克隆ObjectT;


class ClassA<T> : ICloneable 
{ 
    public string Name { get; set; } 
    public T ObjectT { get; set; } 

    public ClassA<T> Clone() 
    { 
     return (ClassA<T>)this.MemberwiseClone(); 
    } 

    object ICloneable.Clone() 
    { 
     return this.Clone(); 
    } 
} 

至于建议,你可以使用一个扩展方法来避免空引用的问题。正如你所看到的,这个问题有几种解决方案。你应该选择一个更适合你的具体问题。

static class CloneableExt 
{ 
    public static T CloneNull<T>(this T obj) where T : class, ICloneable 
    { 
     if (obj == null) return null; 
     return (T)obj.Clone(); 
    } 
} 

编辑1:CloneableExt添加

+0

我认为这种方式会被推荐,我松了一口气,并改为扩展方法来传播空值。 – Jimmy 2009-07-27 21:50:40

+0

确实,扩展方法有这个优点。事实上,你甚至可以对任何'ICloneable'类进行扩展。 – jpbochi 2009-07-27 21:59:28

1

是的,第一个查询不克隆列表中的对象,因此instance将引用list中的实际对象。第二个查询显式构造对象的克隆。

+0

但浅拷贝,对不对? ObjectT仍然是原始实例的参考。 – 2009-07-27 21:44:16

+0

是的,当然。 – 2009-07-27 21:53:06

2

它将是对列表中对象的引用。你可以做的,是使用返回重复导致代码

list.Where(x => x.Name == "A").FirstOrDefault().Clone() 

注意的克隆()函数创建一个副本选择(X => x)是不必要的。克隆将沿

public static ClassA<T> Clone<T>(this ClassA<T> self) { 
    if (self == null) return null; 
    return new ClassA { 
     Name = self.Name, 
     ObjectT = self.ObjectT 
    } 
} 
+0

感谢您的简化版本。但是,对我来说,阅读真的很难。 Where子句确实使它更加明确易读。但是,如果后面的代码x => not-as-instance默认意味着x返回类型,那很酷。不知道要走哪条路。 – 2009-07-27 21:29:24

+0

是的,我改回来了,因为FirstOrDefault不支持那个过载。 – Jimmy 2009-07-27 21:31:16

+0

不会只是创建一个浅拷贝? ObjectT仍然只是一个参考。 – 2009-07-27 21:43:23

0

你在你的第二个例子做的是浅拷贝,这是一个新的实例,但其refernce类型成员仍然refensced。

要做到这一点正确,应实现ICloneable:

class ClassA<T> : System.ICloneable where T : System.ICloneable 
    { 
     public string Name { get; set; } 
     public T ObjT { get; set; } 


     public object Clone() 
     { 
      var clone = new ClassA<T>(); 
      clone.Name = (string)Name.Clone(); 
      clone.ObjT = (T)ObjT.Clone(); 
      return clone; 
     } 
    } 

然后

ClassA<T> instance2 = list.Where(x=> x.Name == "A").First().Clone();