0

I need to pass an IEnumerable and a delegate to my method, and then filter it and return an IEnumerable collection with numbers that pass filter.

Func<int, bool> filter = x => x % 3 == 0;


public static IEnumerable<int> Filter(IEnumerable<int> numbers, Func<int, bool> filter)
{
    foreach (int number in numbers) 
    {
        if (filter.Invoke(number)) 
        {
            // add number to collection
        }
    }

    // return collection
}

I have tried creating a new collection like this:

IEnumerable<int> result = new IEnumerable<int>();

But it is not possible since IEnumerable is an abstract class.

How should I create and return this collection? I know that there is a lot of easy ways to do this, but can't find any.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 5
    Does this answer your question? [What is the yield keyword used for in C#?](https://stackoverflow.com/questions/39476/what-is-the-yield-keyword-used-for-in-c) – SomeBody Jan 28 '21 at 14:28
  • 1
    Something like `numbers.Where(filter)`? You might not even need that method, `Enumerable.Where()` does that (and some more) for you. – Adriano Repetti Jan 28 '21 at 14:28
  • I need a method because my task says so. But that's it, I knew there is an easy way to do it. Thank you. – Josip Maričević Jan 28 '21 at 14:32
  • 2
    You cannot instantiate `IEnumerable` because it is an interface. If you use `List` (which implements `IEnumerable`) you'll be able to add items to it and then return that. – Martin Costello Jan 28 '21 at 14:35
  • I know it is possible, but what if I pass Array to the method, and want array to be returned? – Josip Maričević Jan 28 '21 at 14:47
  • 1
    *But it is not possible since IEnumerable is an abstract class.* - no, actually - `IEnumerable` is an **interface** - not an abstract class ! – marc_s Jan 28 '21 at 14:52

1 Answers1

1

As @somebody pointed out, the right way to do this is to use yield return:

public static IEnumerable<int> Filter(IEnumerable<int> numbers, Func<int, bool> filter) {
    foreach (int number in numbers) {
        if (filter.Invoke(number)) {
            yield return number;
        }
    }
}

When your returned collection (your returned IEnumerable<int>) is enumerated (via a foreach or a call to something like ToList), then you code will be called for the first item. When you get to the yield return, the first number will be returned. When the second pass (and subsequent passes) through the iteration occurs, the code will continue to execute on the line following the yield return.

For what it's worth, your code is pretty much exactly what LINQ's Where extension method does for IEnumerable<T>

Flydog57
  • 6,851
  • 2
  • 17
  • 18