在包装到扩展方法后,LINQ语句不再有效

Dav*_*ave 1 c# linq extension-methods .net-3.5

我需要一个可以采用字符串集合的方法,并将所有特定字符串替换为另一个字符串.

例如,如果我有一个List<string>如下所示:

List<string> strings = new List<string> { "a", "b", "delete", "c", "d", "delete" };
Run Code Online (Sandbox Code Playgroud)

我想用""替换"删除",我会使用这个LINQ语句:

strings = (from s in strings select (s=="delete" ? s=String.Empty : s)).ToList();
Run Code Online (Sandbox Code Playgroud)

它很棒.但后来我想我应该把它作为一种扩展方法,因为我以后可能会再次使用它.在这种情况下,我只想写下面的内容:

strings.ReplaceStringInListWithAnother( "delete", String.Empty);
Run Code Online (Sandbox Code Playgroud)

当我的代码编译,并且LINQ语句在扩展方法内部工作时,当我返回时,集合恢复为其原始内容:

public static void ReplaceStringInListWithAnother( this List<string> my_list, string to_replace, string replace_with)
{
    my_list = (from s in my_list select (s==to_replace ? s=replace_with : s)).ToList();
}
Run Code Online (Sandbox Code Playgroud)

所以看起来我只是修改了List的副本......但是当我查看Pop的代码时,它同样修改了集合,但更改仍然存在,所以我的假设是我的方法的参数声明是正确的.

谁能解释我在这里做错了什么?

Tim*_*mwi 8

您编写的LINQ语句不会修改集合,它实际上会创建一个新集合.

您编写的扩展方法创建此新集合,然后将其丢弃.赋值是多余的:您要分配一个本地参数,该参数在之后立即超出范围.

当您调用该方法时,您也会丢弃其结果而不是将其分配回来.

因此,您应该编写如下方法:

public static List<string> ReplaceStringInListWithAnother(
    this List<string> my_list, string to_replace, string replace_with)
{
    return (from s in my_list select
        (s == to_replace ? replace_with : s)).ToList();
}
Run Code Online (Sandbox Code Playgroud)

和这样的电话:

strings = strings.ReplaceStringInListWithAnother("delete", "");
Run Code Online (Sandbox Code Playgroud)

顺便说一下,通过使它成为通用函数,可以使函数更有用:

public static List<T> ReplaceInList<T>(this List<T> my_list,
    T to_replace, T replace_with) where T : IEquatable<T>
{
    return (from s in my_list select
        (s.Equals(to_replace) ? replace_with : s)).ToList();
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以将它用于其他事情,而不仅仅是strings.此外,您还可以声明使用它IEnumerable<T>而不是List<T>:

public static IEnumerable<T> ReplaceItems<T>(this IEnumerable<T> my_list,
    T to_replace, T replace_with) where T : IEquatable<T>
{
    return from s in my_list select (s.Equals(to_replace) ? replace_with : s);
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以将它用于任何可等项目的集合,而不仅仅是List<T>.请注意List<T>implements IEnumerable<T>,因此您仍然可以将List传递给此函数.如果你想要一个列表,只需.ToList()在调用这个列表后调用.

更新:如果你真的想要替换列表中的元素而不是创建一个新的元素,你仍然可以使用扩展方法,它仍然是通用的,但你不能使用Linq,你不能使用IEnumerable<T>:

public static void ReplaceInList<T>(this List<T> my_list,
    T to_replace, T replace_with) where T : IEquatable<T>
{
    for (int i = 0; i < my_list.Count; i++)
        if (my_list[i].Equals(to_replace))
            my_list[i] = replace_with;
}
Run Code Online (Sandbox Code Playgroud)

这不会返回新列表,而是修改旧列表,因此它具有与原始列表类似的void返回类型.