2012-12-20 24 views
10

我有一個列表,獲取來自操作的一些數據,我將它存儲在內存緩存中。現在我需要另一個列表,其中包含基於某些條件的列表中的一些子數據。如何在不更改原始列表的情況下更改我的新列表?

從下面的代碼可以看出,我在目標列表上做了一些操作。問題是,我對目標列表所做的任何更改也正在對mainList進行。我認爲它的參考是相同的或某事。

我所需要做的就是對目標列表的操作不會影響主列表中的數據。

List<Item> target = mainList; 
SomeOperationFunction(target); 

void List<Item> SomeOperationFunction(List<Item> target) 
{ 
    target.removeat(3); 
    return target; 
} 
+1

你是說在兩個列表中存在的對象都被修改?即你需要克隆()/創建它們的副本,而不是在相同的實例上工作? – Oli

回答

14

您需要克隆的列表中你的方法,因爲List<T>是一類,所以它的引用類型和通過引用傳遞。

例如:

List<Item> SomeOperationFunction(List<Item> target) 
{ 
    List<Item> tmp = target.ToList(); 
    tmp.RemoveAt(3); 
    return tmp; 
} 

或者

List<Item> SomeOperationFunction(List<Item> target) 
{ 
    List<Item> tmp = new List<Item>(target); 
    tmp.RemoveAt(3); 
    return tmp; 
} 

List<Item> SomeOperationFunction(List<Item> target) 
{ 
    List<Item> tmp = new List<Item>(); 
    tmp.AddRange(target); 
    tmp.RemoveAt(3); 
    return tmp; 
} 
+0

這裏select不做任何事情,你只需要'ToList'。 – Servy

+0

完美..謝謝你 – user505210

+1

第三個例子完全錯了。 'tmp' var被用來填充自己。然後你仍然從'target'而不是'tmp''移除元素。 –

1

您的目標變量是一個引用類型。這意味着你對它做的任何事情都會反映在你傳遞給它的列表中。

要做到這一點,您需要在該方法中創建一個新列表,將target內容複製到該列表中,然後在新列表中執行刪除操作。

About Reference and Value Types

4

先建立一個新的列表,並在該操作,因爲名單是引用類型,即當您在函數中傳遞它,你不只是傳遞值,而是傳遞實際的對象本身。

如果你只是分配targetmainList,兩個變量指向同一個對象,所以你需要創建一個新的列表:

List<Item> target = new List<Item>(mainList); 

void List<Item> SomeOperationFunction()是沒有意義的,因爲無論你什麼也不返回(void)或你將返回一個List<T>。因此,要麼從您的方法中刪除返回語句,要麼返回一個新的List<Item>。在後一種情況下,我會爲改寫這個:

List<Item> target = SomeOperationFunction(mainList); 

List<Item> SomeOperationFunction(List<Item> target) 
{ 
    var newList = new List<Item>(target); 
    newList.RemoveAt(3); 
    return newList; 
} 
+0

'List是一個引用類型,因此通過引用傳遞 - 是循環的。如果你這樣說,你將不得不解釋一個引用類型是什麼,或者鏈接到一個引用(不是雙關語)。 –

+0

@RobertHarvey謝謝你指出。 – Residuum

+0

當然,除非有'ref'關鍵字,否則您仍然通過值傳遞引用,而不是引用。區分傳遞參考值與參考值是很重要的。 – Servy

6

你需要讓列表的副本,以便更改副本不會影響原來的。最簡單的方法是在System.Linq中使用ToList擴展方法。

var newList = SomeOperationFunction(target.ToList()); 
+3

@Bernhof:這是正確的;但是,請記住,如果項目是引用類型,則列表仍然保留對相同項目的引用。因此對項目本身的更改仍會影響這兩個列表中的項目。 –

+0

要做的一點,但我會爭辯說'SomeOperationFunction'應該處理複製。如果函數*返回一個'列表',那麼人們會期待儘可能多。 – bernhof

+0

@Bernhof也許,是的。鑑於像其他5人一樣建議改變,我沒有看到需要重申它。重點僅僅是複製需要在某個地方進行。 – Servy

0

你所看到的原始列表進行修改,因爲在默認情況下,任何非基本對象,通過引用傳遞(它實際上是按值傳遞,該值作爲參考,但那是另一回事)。

你需要做的是複製對象。這個問題將幫助你用C#克隆一個列表的代碼:How do I clone a generic list in C#?

+1

如果列表通過引用或按值傳遞,則完全沒有關係。在任何情況下,'target'都將是一個指向原始列表的引用。如果您在'target = null;'中指定一個新的值給'target',那麼通過值/引用只會產生差異。 –

+0

@ OlivierJacot-Descombes - 這就是爲什麼我簡化說它是通過引用。括號中的註釋只是指出,除非明確使用'ref'關鍵字,否則C#中的所有變量都是按值傳遞的。恰巧,用於複雜類型的'值'是一個指針。 –

+0

'string'是一個原始類型,但是是一個引用類型(它是不可變的)。 'System.Drawing.Point'不是一個原始類型,而是一個值類型(一個結構體)。 –

0

由於List是一個引用類型,傳遞給函數的是對原始列表的引用。

有關參數如何在C#中傳遞的更多信息,請參閱此MSDN article

爲了達到你想要的效果,你應該在SomeOperationFunction中創建一個列表的副本,然後返回它。一個簡單的例子:

void List<Item> SomeOperationFunction(List<Item> target) 
{ 
    var newList = new List<Item>(target); 
    newList.RemoveAt(3); 
    return newList; // return copy of list 
} 

正如奧利弗Jacot-Descombes在評論到另一個答案指出的那樣,在牢記

[...]列表中仍持有的引用是非常重要的如果 這些項目是引用類型,則返回相同的項目。所以對項目本身 的更改仍會影響這兩個列表中的項目。

+1

也許你應該花一些時間閱讀你自己的鏈接。引用類型不是通過引用傳遞的,它們通過值傳遞,就像值類型一樣,但傳遞的*是引用*。這是一個非常重要的區別。 – Servy

+0

'弄錯了,我明白其中的差異,並編輯了答案,以避免誤解。 – bernhof

+0

你爲什麼刪除鏈接?這是一個足夠好的鏈接。 – Servy

0

不是指定mainList的目標,我會做:target.AddRange(mainList);

然後,你將擁有的物品,而不是對列表的引用的副本。

+0

爲什麼要調用'ToArray'? 'AddRange'接受列表已經實現的'IEnumerable'。 – Servy

+0

我跑到實例(也許.net 2.0或3.5),其中'AddRange'只有一個數組。如果最新的框架接受「IEnumerable」,我很樂意修改我的答案。 –

+0

IEnumerable '.NET 2.0的重載。 [鏈接](http://msdn.microsoft.com/en-us/library/z883w3dc(V = VS.80)的.aspx)。唯一沒有的版本是1.0。 – Servy

0

只要確保使用通過複製源列表元素創建的列表來初始化新列表。

List<Item> target = mainList;應該List<item> target = new List<Item>(mainList);

0

你需要做列表的副本,因爲在你所做的只是路過圍繞你的原代碼,當你正確懷疑,參考(有人叫它一個指針)。

你既可以調用新的名單上的構造,通過原來的列表作爲參數:

List<Item> SomeOperationFunction(List<Item> target) 
{ 
    List<Item> result = new List<Item>(target); 
    result.removeat(3); 
    return result; 
} 

或者創建一個MemberWiseClone:返回

List<Item> SomeOperationFunction(List<Item> target) 
{ 
    List<Item> result = target.MemberWiseClone(); 
    result.removeat(3); 
    return result; 
} 

而且,你是不是存儲所以你可能也想修改那個部分(你聲明方法爲void,它不應該返回任何東西,但是在它內部你要返回一個對象)。 你應該調用的方法是這樣的:

List<Item> target = SomeOperationFunction(mainList); 

注:列表中的的元素不會被複制(只有自己參考複製),所以修改元素的內部狀態將影響名單。

0

即使您創建了新列表,對新列表中項目的引用仍將指向舊列表中的項目,因此如果需要使用新引用的新列表,我喜歡使用此擴展方法。 ..

public static IEnumerable<T> Clone<T>(this IEnumerable<T> target) where T : ICloneable 
{ 
    If (target.IsNull()) 
     throw new ArgumentException(); 

    List<T> retVal = new List<T>(); 

    foreach (T currentItem in target) 
     retVal.Add((T)(currentItem.Clone())); 

    return retVal.AsEnumerable(); 
} 
相關問題