views:

142

answers:

10

I wonder if its possible to remove all the objects from the same kind from a generic List using extension methods. something like this code:

public static Remove<T>(this List<[any type]> list)
{
    // some code to remove the objects of type T from the list
}

I can do this by using the following code:

public static Remove<T, K>(this List<K> list)
{
    // some code to remove the objects of type T from the List<K>
}

but I want to just use on type (T), without need to specify any type K. by doing that the user can use this extension method by simply write this:

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

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

a extension method which I can use to do something exactly like the above code is what I really need here.

best regards

A: 

What you're trying to do is have type inference work on one type parameter, but specify the other explicitly. I'm afraid C# doesn't allow that.

The form using the non-generic IList given by Justin will work because it only has the one type parameter (the type of element to remove). It's pretty ugly though.

Is this something you actually need often enough that you're bothered about specifying both type arguments?

Jon Skeet
+2  A: 

Do you need to provide the filter type as a generic?

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));
Matthew Whited
Err, this will just produce an `IEnumerable<T>` that represents all of the items *not* of the specified type. Not sure how that works to the OP's original purpose.
Adam Robinson
I didn't see a return type so I assumed he wanted it to chain objects together. If you just want to remove element without the return see Justin's answer
Matthew Whited
+6  A: 

Not sure if this will work or not...but it's worth a shot (I can't compile to double check):

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);
    }
}

Or if you need something a little more strict (rather than any object that can be cast to the type), you could try:

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);
    }
}

In theory, you should be good to go. List<T> implements IList which implements IEnumerable. IList provides Remove() and IEnumerable provides the extension method.

Be aware that this could most definitely produce unexpected results depending on the types in the collection. I agree with Jon Skeet...it's most definitely ugly.

Justin Niessner
so we no longer have to provide return types ;o)
Matthew Whited
OfType will return every object that can be cast to type. This may remove too many elements...
Reed Copsey
It works with a couple of tweaks :)
Jon Skeet
This works, but I would do a reverse for loop and omit the `var toRemove` query. `int maxIndex = list.Count - 1;` and then `for (int i = maxIndex; i >= 0; i--)` `{ if (list[i].GetType() == typeof(T)) list.RemoveAt(i); }`
Anthony Pegram
This works fine. this is the answer.
Nima Rikhtegar
@Nima: Just realize that this won't remove items of "Type T", but all items **castable** to type T. If you pass a base class, all base class + subclass elements will be removed, for example.
Reed Copsey
in the above code just first check to see if IList is null before using it. this will create a more reliable code. so change to code to some thing like this: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); } }
Nima Rikhtegar
+1  A: 

I don't recommend this, as it seems like an API which will be very difficult to understand and maintain.

That being said, the following should work:

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);
     }
}
Reed Copsey
Why call `ToList`?
StriplingWarrior
@StriplingWarrior: You need to evaluate the LINQ query, or it'll potentially throw an exception once you remove an element, since you'll still be evaluating it as you're editing the collection.
Reed Copsey
Good answer. Thanks.
StriplingWarrior
A: 

What you really want to do is have an extension method like so:

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);
    } 
}

Note that you can only use this for List<T>, since it implements the non-generic IList interface. The IList<T> interface does not derive from the non-generic IList interface.

casperOne
An extension method needs the `this` keyword before the first parameter.
StriplingWarrior
A: 

Here is yet another variant. Return only used in case you want to chain the method.

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

thanks to the greate answer of Justin Niessner I managed to do it by using the following code:

public static void Remove<T>(this IList list)
{
    ArrayList deleteList = new ArrayList();

    foreach (var item in list)
        if (item.GetType() == typeof(T))
            deleteList.Add(item);

    for (int i = 0; i < deleteList.Count; i++)
        list.Remove(deleteList[i]);        
}

in the Justin Niessner answer there is one problem. you can not delete an enumarator item in foreach block.

Nima Rikhtegar
A: 

I would probably do something like:

enumerable.Cast<object>().Where(o => !(o is T))

And skip the fact that enumerable is a list. (Cast here is being used to ensure that Where works, if you already have a generic enumerable then you can skip it)

Bill Barry
A: 

Step backwards through the list and remove the ones that match the undesired type.

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 );
        }
    }
}

If you are always going to be using List<object> you can simplify things a little more.

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

Happy Coding!

Jerod Houghtelling
A: 

Not sure if this will do

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

Can be invoked as below

  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));
        }
josephj1989