2009-07-27 107 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();