我对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.不确定我的理解是否正确。有什么意见吗?
答案 0 :(得分:2)
它将是对列表中对象的引用。你可以做的是使用Clone()函数创建一个副本,该函数返回一个副本,产生代码
list.Where(x => x.Name == "A").FirstOrDefault().Clone()
请注意,选择(x =&gt; 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
}
}
答案 1 :(得分:1)
是的,第一个查询不会克隆列表中的对象,因此instance
将引用list
中的实际对象。第二个查询显式构造了对象的克隆。
答案 2 :(得分:1)
由于您的ClassA<T>
是class
(而不是struct
),instance1
是对list
内的同一对象的引用。< / p>
关于策略,如果您不希望列表中的用户能够搞清楚列表元素,我会建议这些替代方案:
将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
答案 3 :(得分:0)
你在第二个例子中所做的是浅拷贝,这是一个新的实例,但其引用类型成员仍然被重新修改。
要做得好,你应该实施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();