Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement IEnumerable on property

Tags:

c#

ienumerable

Assuming the class

public class Foo 
{
    public List<Bar> Bar = new List<Bar>();
    public string Something;
    public TStatus Status;
}

Bar is a class defined as

public class Bar
{
    public string Code;
    public string Message;
    public TStatus Status;
}

I need to iterate the list Bar but I cant use a foreach because I need to implement IEnumerable I am a bit of a newbie and am having difficulty working out how to implement it, any help appreciated.

like image 676
Cookie Avatar asked Nov 30 '22 09:11

Cookie


2 Answers

A couple of things:

1.) As for your actual question, you have two options:

You can iterate over the Bar field (or property, if you change it to a property as outlined below) in your code:

Foo foo = new Foo();

foreach(Bar bar in foo.Bar)
{
    ...
}

Or you can make Foo implement IEnumerable<Bar> (this is more complicated):

public class Foo : IEnumerable<Bar>
{
    .. your existing code goes here

    public IEnumerator<Bar> GetEnumerator()
    {
        return Bar.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return Bar.GetEnumerator();
    }
}

This will allow you to do this:

Foo foo = new Foo();

foreach(Bar bar in foo)
{

}

Personally, I would recommend the first option. It's simpler, and unless Foo really is (rather than it just has) a collection of Bar objects, then it seems more intuitive.

2.) What you have above are not properties, they're public fields. A property has a getter and a setter, and looks something like this:

public class Foo
{
    public string Something
    {
        get { return ...; }
        set { ... = value; }
    }
}

Properties usually operate on a single variable, called a backing store. For the above property, something like this:

public class Foo
{
    private string something;

    public string Something
    {
        get 
        { 
            return something; 
        }
        set  
        { 
            something = value; 
        }
    }
}

Strictly speaking, you could put absolutely any block of code that returns a string in the get block, and you can put anything at all in the set block (including nothing, though that's not a good idea).

Properties allow you to define something that behaves like a field (since you can assign it using the equals sign) but uses the behavior you specify when its value is retrieved or set. However, one of the issues with very simple properties (that just directly get/set a backing variable without any other logic) is that it's a little verbose for what it actually does. That's why C# 3.0 introduced the concept of auto properties, which look like this:

public class Foo
{
    public string Something { get; set; }
}

Here, Something looks a bit like a field, but it's actually a property. The C# compiler recognizes this syntax (replacing the code blocks for both get and set with just a semicolon) and creates a backing variable for you and inserts the code to read from it and write to it.

like image 180
Adam Robinson Avatar answered Dec 15 '22 22:12

Adam Robinson


I'll make a leap here that what you're trying to do is add a property to your Foo class that returns an IEnumerable, or IEnumerable<Bar> and that this property needs to perform some kind of filtering on the list of Bar objects contained in the Foo.Bar field.

EDIT: If this doesn't sound like what you're trying to do, then this answer may not be for you. Please don't let my answer lead you to further confusion.

Here are my assumptions about the problem:

  1. Both Foo and Bar are nested classes within another class, and TStatus is a generic parameter on that containing class.
  2. You want to add a property to Foo that returns a collection of Bar objects that have a Status property equal to the Foo object's Status property. I'm just making a guess here, but I thought that might be why you have a Status property on both Foo and Bar.

Below is a complete implementation that provides a BarsFilteredByStatus property on the Foo class. This uses .NET iterator functionality to provided a filtered collection of Bar objects where Bar.Status equals Foo.Status. If this seems like something along the lines of what you're after, then go ahead, take this code and play around with it.

// an example status enum
enum SomeStatus
{
    Open,
    Closed,
    Funky,
}

// Blarg<TStatus> is just some container class.  This isn't necessary, but it allows TStatus to be a generic parameter rather than a specific type.
class Blarg<TStatus>
{
    public class Bar
    {
        public string Code;
        public string Message;
        public TStatus Status;
    }

    public class Foo
    {
        public List<Bar> Bar = new List<Bar>();
        public string Something;
        public TStatus Status;

        public IEnumerable<Bar> BarsFilteredByStatus
        {
            get
            {
                // return a filtered collection of bars where Bar.Status equals Foo.Status
                foreach (var bar in this.Bar)
                {
                    if (this.Status.Equals(bar.Status))
                        yield return bar;
                }
            }
        }
    }
}

// Code from this point on is a usage example

class Program
{
    static void Main(string[] args)
    {
        // set up some example data
        var bars = new List<Blarg<SomeStatus>.Bar>();
        bars.Add(new Blarg<SomeStatus>.Bar { Code = "123", Status = SomeStatus.Open });
        bars.Add(new Blarg<SomeStatus>.Bar { Code = "234", Status = SomeStatus.Closed });
        bars.Add(new Blarg<SomeStatus>.Bar { Code = "345", Status = SomeStatus.Funky });
        bars.Add(new Blarg<SomeStatus>.Bar { Code = "456", Status = SomeStatus.Open });
        bars.Add(new Blarg<SomeStatus>.Bar { Code = "567", Status = SomeStatus.Funky });

        // create a Foo object
        Blarg<SomeStatus>.Foo foo = new Blarg<SomeStatus>.Foo
        {
            Bar = bars,
            Status = SomeStatus.Open,
        };

        // now iterate over the Foo.BarsFilteredByStatus property
        // This will iterate over all Bar objects contained within Foo.Bar where Bar.Status equals Foo.Status
        foreach (var bar in foo.BarsFilteredByStatus)
        {
            Console.WriteLine(bar.Code);
        }
    }
}
like image 32
Dr. Wily's Apprentice Avatar answered Dec 15 '22 23:12

Dr. Wily's Apprentice