使用扩展方法从C#的List<T>中删除某种类型的对象?

8
我想知道是否可以使用扩展方法从通用列表中删除所有相同种类的对象,就像这段代码一样:
public static Remove<T>(this List<[any type]> list)
{
    // some code to remove the objects of type T from the list
}

我可以通过使用以下代码来完成此操作:
public static Remove<T, K>(this List<K> list)
{
    // some code to remove the objects of type T from the List<K>
}

但我希望只使用一个类型(T),而无需指定任何类型K。通过这样做,用户可以通过简单地编写以下内容来使用此扩展方法:

List<object> list = new List<object>();
list.Add(1);
list.Add("text");

// remove all int type objects from the list
list.Remove<int>();

我需要一个扩展方法,能够像上面的代码一样做某件事情,这正是我所需要的。

最好的问候

9个回答

6

不确定这是否有效...但值得一试(我无法编译以进行双重检查):

public static void Remove<T>(this IList list)
{
    if(list != null)
    {
        var toRemove = list.OfType<T>().ToList();

        foreach(var item in toRemove)
            list.Remove(item);
    }
}

或者,如果你需要更严格的匹配(而不仅仅是可以转换为该类型的任何对象),可以尝试使用以下代码:

public static void Remove<T>(this IList list)
{
    if(list != null)
    {
        var toRemove = list.Where(i => typeof(i) == typeof(T)).ToList();

        foreach(var item in toRemove)
            list.Remove(item);
    }
}

理论上来说,你应该可以顺利进行。 List<T> 实现了 IList,而 IList 又实现了 IEnumerable。IList 提供了 Remove() 方法,而 IEnumerable 则提供了扩展方法。
请注意,这可能会根据集合中的类型产生意想不到的结果。我同意 Jon Skeet 的观点......这确实很丑陋。

所以我们不再需要提供返回类型 ;o) - Matthew Whited
OfType会返回可以转换为指定类型的所有对象。这可能会删除太多元素... - Reed Copsey
2
@Nima:刚刚意识到这个操作并不能删除“Type T”的所有项目,而是删除所有可转换为类型T的项目。例如,如果您传递一个基类,则将删除所有基类+子类元素。 - Reed Copsey
在上述代码中,使用前先检查 IList 是否为 null,这将创建更可靠的代码。因此,更改代码为以下内容:public static void Remove<T>(this IList list) { if (list != null) { var toRemove = list.OfType<T>().ToList(); foreach(var item in toRemove) list.Remove(item); } } - user354583

4

您需要将过滤器类型作为通用类型提供吗?

public static IEnumerable<T> Remove<T>(this IEnumerable<T> items, 
                                            Type removeThese)
{
    return items.Where(i => !removeThese.IsInstanceOfType(i));
}

// usage
var newSet = mylist.Remove(typeof(int));

错误,这只会生成一个IEnumerable<T>,表示所有不属于指定类型的项目。不确定如何与原始目的相符。 - Adam Robinson
我没有看到返回类型,所以我假设他想要将对象链接在一起。如果你只想删除元素而不返回,请参考Justin的答案。 - Matthew Whited

1

我不建议这样做,因为它似乎是一个非常难以理解和维护的API。

话虽如此,以下方法应该可以工作:

public static void Remove<T>(this IList list)
{
     Type type = typeof(T);
     var itemsToRemove = list.Cast<object>.Where(o => o.GetType() == type).ToList();
     foreach(var item in itemsToRemove)
     {
          list.Remove(item);
     }
}

2
@StriplingWarrior:你需要评估LINQ查询,否则一旦你删除一个元素,它可能会抛出异常,因为你在编辑集合时仍然在评估它。 - Reed Copsey

1

向后遍历列表并删除与不需要的类型匹配的元素。

public static void RemoveAllOfType<T>( this IList list )
{
    for( int index = list.Count - 1; index >= 0; index -- )
    {
        if( list[index] is T )
        {
            list.RemoveAt( index );
        }
    }
}

如果您总是使用 List<object>,您可以更简化事情。

public static void RemoveAllOfType<T>( this List<object> list )
{
    list.RemoveAll( item => item is T );
}

编程愉快!


0
你真正想要做的是创建一个像这样的扩展方法:
public static void Remove<T>(this IList list)
{
    // Cycle through everything, comparing the types.
    for (int index = list.Count; index >= 0; --index)
    {
        // Get the object.
        object item = list[index];

        // If the item is null, continue.
        if (item == null) continue;

        // Remove the item if the types match.
        if (typeof(T) == item.GetType()) list.RemoveAt(index);
    } 
}

请注意,您只能将此用于 List<T>,因为它实现了非泛型 IList 接口。而 IList<T> 接口并不继承自非泛型 IList 接口。

一个扩展方法需要在第一个参数之前加上 this 关键字。 - StriplingWarrior

0

这是另一种变体。仅在您想链接该方法时才返回使用。

public static List<TList> Remove<TList, TRemove>(this List<TList> list)
{
    list.RemoveAll(item => item is TRemove);
    return list;
}

0
我可能会这样做:
enumerable.Cast<object>().Where(o => !(o is T))

忽略可枚举是列表这一事实。(此处的转换用于确保 Where 起作用,如果您已经有一个通用的可枚举对象,则可以跳过它)


0

不确定这样行不行

static class test
    {
        public static void RemAll<T>(this List<Object> self, T t) where T : Type
        {
            self.RemoveAll(x => x.GetType()==t);
        }
    }

可以按以下方式调用

  static void Main(string[] args)
        {
            List<object> ll = new List<object>() { 1, 2, "abcd", "xyz", 22.3, 900, "junk" };
            ll.RemAll(typeof(int));
            ll.ForEach(x => Console.WriteLine(x));
        }

0

你想要的是让类型推断只对一个类型参数起作用,但另一个类型参数需要显式指定。很抱歉,C# 不允许这样做。

使用 Justin 给出的非泛型 IList 的形式可以工作,因为它只有一个类型参数(要删除的元素的类型)。不过这种方式相当丑陋。

你真的经常需要这样做吗?需要同时指定两个类型参数吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接