ss7
ss7

Reputation: 3012

Implementing IEnumerable, Lists

I found this MSDN tutorial

It describes implementing IEnumerable in order to use the object in a foreach loop. My question is, in the example's main method it uses a Person array to initialize the People class. Can I use a List< ClientEntry> rather than an array of ClientEntry? What is required to implement a List for a certain class?

Upvotes: 0

Views: 1574

Answers (3)

Ryan Mann
Ryan Mann

Reputation: 5357

You don't need to create a custom collection to use a class in a list. There is already a generic collection out of the box that can be used for any class (via generics).

You can do this now,

[Flags()]
public enum AddressTypeValue : ushort
{
    Personal = 0,
    Business = 1,
    POBox = 2,
    Inertnational = 4
}
public class Address
{
    public AddressTypeValue AddressType { get; set; }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }

    public Person() 
    {
        Address = new Address() { AddressType == AddressTypeValue.Personal };
    }
}

//some code using it in a list

List<Person> people = new List<Person>();

public List<Person> GetPeopleMatchingFirstName(string firstNameToMatch)
{
    var people = DBContext.People.Where(p => p.FirstName.BegintsWith(firstNameToMatch) || p.FirstName.EndsWith(firstNameToMatch) || p.FirstName.Equals(firstNameToMatch, StringComparison.InvariantCultureIgnoreCase);
    return people.ToList();
}

Now if you want a strongly typed collection where you want to have custom logic for Add/Delete/Insert/Indexing etc, then and only then would you need to implement IList or IEnumerable.

There is any easier way to do that though because you just implement List via inheritance and override the thing you want to change. In the event the behavior you want to change is not virtual (cannot be overridden) then you might need a custom implementation of List.

public class MyCustomListOfPeople : List<Person>
{
    public override Add(Person person)
    {
        //send email that a person was added or w/e you want to do here
        base.Add(person);
    }
}

If you want a readonly list, where things can't be added then you can just use ReadOnlyCollection where you pass in a base list. Once constructed nothing can be added or removed from the ReadOnlyCollection. This is useful for things like state lists, zip codes, report data, etc etc.

It is also easy to wrap a List and expose it as IEnumerable without allowing access to it's add/delete etc methods.

public class AnotherListOfPeople : IEnumerable<Person>
{
    private List<Person> internalList = new List<Person>();

    //internal logic to manipulate people

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

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return internalList.GetEnumerator();
    }
}

Doing the latter allows you to surface a private list's built in enumerator to something else.. So the enumerator interfaces are being exposed by the wrapping class those allowing your class to be enumerated with Linq and foreach while blocking callers from having direct access to the List.

There are many more ways you can leverage the power of a List or IEnumerable, but generally you can just use a List for whatever you need and rarely need to wrap one or implement the interfaces for a custom collection.

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476503

As one can see in the documentation about List<T>, this class has the signature:

[SerializableAttribute]
[DebuggerDisplayAttribute("Count = {Count}")]
public class List<T> : IList<T>, ICollection<T>, 
    IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, 
    IReadOnlyCollection<T>

As you can see, it already implement IEnumerable<T> and IEnumerable. This means every List<T> instance can be used in a foreach loop:

List<Person> people = new List<Person>();
//add people
foreach(Person p in people) {
}

Furthermore there are no restrictions on T (you can use any type you like). Otherwise the signature should contain a

public class OtherList<T> : IEnumerable<T> where T : class

the where T : ... means T should satisfy a certain constraint. There is no need at all to start implementing List<T>s yourself.

A List<T> is a class. Since C# does not allow multiple inheritance, one can only make a Peolpe an element from List<People> by adding inheritance:

// Collection of Person objects. This class 
// implements IEnumerable so that it can be used 
// with ForEach syntax. 
public class People : List<Person> {

    public People(Person[] pArray) {
        this.AddRange(pArray);
    }

}

In that case you don't have to implement IEnumerable<T> and other interfaces. But only if you want to add additional behavior (or modify some behavior) there is no reason to use People over List<Person>.

Upvotes: 2

thinker
thinker

Reputation: 211

It is surely possible to use a List<Person> instead of Person[]. You will just need to replace all the backing variables, changing their type from array to List and change relevant code as well. The point however, is that List is already enumerable so it doesn't make much sense to implement another IEnumerable using a List as internal storage, unless you are doing something special like skipping certain elements based on dynamic conditions etc.

Upvotes: 2

Related Questions