2010-06-18 19 views
3

我有第三方api,它有一個返回類中不同項目的枚舉器的類。枚舉器的問題,任何方式來避免兩個循環?

我需要刪除該枚舉器中的項目,所以我不能使用「for each」。我能想到的唯一選擇是通過遍歷枚舉來獲取計數,然後運行正常的for循環來刪除項目。

任何人都知道避免兩個循環的方法?

感謝

[更新]遺憾的註釋中的混亂,但安德烈下面是正確的。

下面是一些僞代碼了我的頭,將無法工作,爲此我期待這將不涉及兩個循環,但我想這是不可能的解決方案:

for each (myProperty in MyProperty) 
{ 
if (checking some criteria here) 
    MyProperty.Remove(myProperty) 
} 

myProperty的是實現枚舉器和remove方法的第三方類。

+1

呃,你**不能從** IEnumerator中刪除某些東西。你可以發佈一些代碼嗎? – 2010-06-18 11:08:27

+1

@Neil Barnwell他意味着有枚舉器和一些刪除元素的方法。在枚舉 – Andrey 2010-06-18 11:09:59

+0

@Andrey時,你無法從集合中刪除@Andrey他沒有說**有一些其他方法可以刪除項目 - 他說他想「刪除該枚舉器中的項目」。作出這樣的假設,你的回答是完全正確的,我只是想澄清一些細節,以防他要求解決不可解決的問題。 – 2010-06-18 11:11:33

回答

6

常見的模式是做這樣的事情:如果你知道這是一個集合

List<Item> forDeletion = new List<Item>(); 

foreach (Item i in somelist) 
    if (condition for deletion) forDeletion.Add(i); 

foreach (Item i in forDeletion) 
    somelist.Remove(i); //or how do you delete items 
+0

嗯,我想避免兩次循環。也許如果它是一個集合而不是一個枚舉器,那麼只有一個for循環就足夠了? – pug 2010-06-19 06:26:53

+0

@pug只是嘗試。創建一個簡單的列表並刪除項目,同時迭代。 – Andrey 2010-06-19 08:04:47

3

循環一次,並創建第二個數組,其中包含不應刪除的項目。

+1

**應**還是**不應**?您應該考慮一旦您掌握了該數組,就應該提及該做什麼。 – 2010-06-18 11:09:50

+0

好像第二個數組變成最終結果。 – Chris 2010-06-19 06:29:47

2

,你可以去恢復爲:

for (int i = items.Count - 1; i >= 0; i--) 
{ 
    items.RemoveAt(i); 
} 

否則,你就必須做兩循環。

1

無法從枚舉器中刪除項目。你可以做的是複製或過濾(或兩者)整個枚舉序列的內容。 您可以通過使用LINQ實現這一點,就水木清華這樣的:

YourEnumerationReturningFunction().Where(item => yourRemovalCriteria); 
1

你能在API闡述和API調用您正在使用?

如果您收到IEnumerator<T>IEnumerable<T>您無法從枚舉數後面的序列中刪除任何項目,因爲沒有方法可以這樣做。你當然不應該依賴向下投射一個接收到的對象,因爲實現可能會改變。根據需要(實際上是一個精心設計的API不應該暴露可變對象持有的內部狀態都沒有。)

如果您收到IList<T>或類似的,你可以只使用一個正常for循環從後到前和刪除的項目的東西,因爲有沒有迭代器哪個狀態可能被破壞。 (這裏再次左右暴露可變狀態應適用該規則 - 修改返回的集合不應該改變任何狀態的。)

+0

我的評論的確切點在上面 - 他談論的是統計員,每個人都假設他有不同的界面,並且正在回答一個不同的問題。當然他們可能是對的,但OP還沒有確認。 – 2010-06-18 11:13:54

2

您可以創建這樣的事情:

 public IEnumerable<item> GetMyList() 
    { 
     foreach (var x in thirdParty) 
     { 
      if (x == ignore) 
       continue; 
      yield return x; 
     } 

    } 
+0

作爲一個LINQ函數會更有效。恕我直言:返回thirdParty.Where(x => x!=忽略); – dbemerlin 2010-06-18 11:33:24

0

IEnumerator.Count()將在運行決定 - 它需要做什麼 - 列舉計算或反映,看看它是一個集合和調用。這種方式。

我喜歡SJoerd的建議,但我擔心我們可能會談論多少物品。

0

爲何不像..

// you don't want 2 and 3 
IEnumerable<int> fromAPI = Enumerable.Range(0, 10); 
IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 }); 
0

一個乾淨的,可讀的方式做到這一點如下(我在第三方容器的API在這裏猜測,因爲你沒有指定它。)

foreach(var delItem in ThirdPartyContainer.Items 
         .Where(item=>ShouldIDeleteThis(item)) 
         //or: .Where(ShouldIDeleteThis) 
         .ToArray()) { 
    ThirdPartyContainer.Remove(delItem); 
} 

.ToArray()的調用確保被刪除的所有項目的foreach迭代開始前已經被貪婪的緩存。

在幕後,這涉及到一個數組和一個額外的迭代,但這通常很便宜,而且這個方法優於其他答案的優點是它對普通枚舉類型有效,並且不涉及棘手的mutable狀態問題難以閱讀,容易出錯。

相比之下,反向迭代,雖然不是火箭科學,但更容易出現逐個錯誤並且難以閱讀;它也依賴於集合的內部,例如在刪除之間不改變順序(例如,最好不要成爲二進制堆)。手動添加應該刪除的項目到臨時列表只是不必要的代碼 - 這就是.ToArray()會做的很好:-)。

2

我需要在枚舉只要這是不是一個問題,一個單一的項目中刪除項目

。規則是你在修改集合後不能繼續迭代。因此:

foreach (var item in collection) { 
    if (item.Equals(toRemove) { 
     collection.Remove(toRemove); 
     break;  // <== stop iterating!! 
    } 
} 
0

枚舉器總是有一個指向真實集合的私有字段。
你可以通過reflection.修改它。
玩得開心。