C#扩展方法

时间:2011-08-18 18:56:51

标签: c# .net lambda extension-methods ienumerable

我目前正在尝试编写扩展方法,但它似乎没有按预期运行。在我们深入研究之前,这是我的代码:

 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对象中。有什么提示吗?

提前致谢,
桑尼

4 个答案:

答案 0 :(得分: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 :(得分: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);

答案 2 :(得分:1)

尝试这个有一个有用的List.RemoveAll(谓词匹配)方法,我认为这是为此设计的:http://msdn.microsoft.com/en-us/library/wdka673a.aspx

所以只需在你拥有的列表中使用它。

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

或您的扩展方法返回所需的可枚举,您可以使用它。

答案 3 :(得分: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));
}