2011-08-18 212 views
2

我目前正在寫一個擴展方法,但似乎並沒有被作爲操作意圖。我們陷得更深層次之前,這裏是我的代碼有:C#擴展方法

public static void Remove<T>(this IEnumerable<T> source, Func<T, bool> predicate) 
{ 
    var items = source.Where(predicate); 

    source = source.Where(t => !items.Contains(t)); 
} 

的願望是,我可以調用任何IEnumerable和匹配的謂詞,然後從集合中刪除所有項目這個擴展方法。我厭倦了通過集合迭代來找到匹配的項目,然後一次刪除它們,以避免在通過它枚舉時改變集合...

無論如何...當我逐步完成代碼時,似乎工作。在現有方法之前,source具有正確數量的刪除項目。但是,當我返回調用代碼時,所有項目仍然存在於我的原始IEnumerable對象中。有小費嗎?

由於提前,
桑尼

+0

還有一件事...... Contains方法是我寫的另一個擴展方法(它按照預期工作)。 –

+0

順便說一句,不知道你是否知道它,但LINQ確實有一個Contains方法:http://msdn.microsoft.com/en-us/library/bb357185.aspx –

回答

11

不能做,你必須原來寫的方式,你正在服用的參考變量(source),並使其指一個新的實例。這會修改本地參考source而不是通過原始的參數。

記住引用類型在C#中,通過方案的默認參數是按值傳遞(其中傳遞的價值爲參考)。

比方說,你在一個變量x這種方法,它指的是原來的列表和列表住在理論位置1000通過,這意味着源是一個新的參照原始列表生活在位置1000

現在,當你說:

source = source.Where(....); 

你是一個新的列表分配source(在位置2000說),但僅影響source點,而不是x你通過

爲了解決這個問題作爲一個擴展方法,你就真的要return新序列代替:

public static IEnumerable<T> Remove<T>(this IEnumerable<T> source, Func<T, bool> predicate) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (predicate == null) throw new ArgumentNullException("predicate"); 

    // you can also collapse your logic to returning the opposite result of your predicate 
    return source.Where(x => !predicate(x)); 
} 

這一切都是假設你想保持它完全通用的IEnumerable<T>,你在你的問題問。顯然,在其他例子如也指出,如果你只關心List<T>有一個烤入RemoveAll()方法。

+1

事實上,我會可能會將它命名爲WhereNot()而不是Remove(),因爲Remove()意味着它會改變原始列表,而WhereNot()與Where()有很好的平行聲音... –

+0

有關命名的好處。我認爲這種方法在目前的形式中是多餘的,因爲現在它只是一個'Where()'包裝器(帶有免費的'!'!) – dlev

+1

它是,但如果你只是想傳遞一個方法組,作爲一個謂詞而不是一個lambda否定方法組到Where()。這是相當微不足道的,但我可以看到有人想說:'WhereNot(MyPredicateMethod)'而不是'Where(x =>!MyPredicateMethod(x))' –

1

試試這個有一個有用的List.RemoveAll(謂詞匹配)方法,我認爲是專爲這樣的:http://msdn.microsoft.com/en-us/library/wdka673a.aspx

因此就使用這個,你必須在名單上。

source.RemoveAll(t => !items.Contains(t)) 

或者您的擴展方法返回所需的枚舉值,您可以使用它。

4

這種擴展應該由返回一個新的序列來實現。這樣,你可以融入序列操作鏈:

public static IEnumerable<T> Remove<T>(this IEnumerable<T> source, Func<T, bool> predicate) 
{ 
    return source.Where(t => !predicate(t)); 
} 

var query = mySequence.Select(x => x.Y).Remove(x => x == 2).Select(x => 2*x); 

現在的方法是什麼,但一個包裝周圍Where(),這顯然是無益的。你可能會考慮擺脫它。

如果您想實際更新底層集合(假設即使存在),那麼您不能這樣做,因爲IEnumerable<T>不提供任何方式來更改其內容。你將不得不做這樣的事情:

var myNewList = new List<int>(oldList.Remove(x => x == 2)); 

最後,如果你List<T>工作,你可以使用RemoveAll()方法實際上是從列表中刪除項目:

int numberOfItemsRemoved = myList.RemoveAll(x => x == 2); 
0

這是因爲IEnumerable的是不可改變的 你必須從你的Remove方法返回另一個序列這個工作:

public static IEnumerable<T> Remove<T>(this IEnumerable<T> source, Func<T, bool> predicate) 
{ 
    var items = source.Where(predicate); 

    return source.Where(t => !items.Contains(t)); 
}