我目前正在尝试编写扩展方法,但它似乎没有按预期运行。在我们深入研究之前,这是我的代码:
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 :(得分: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));
}