简体   繁体   中英

Some clarifications about IEnumerable interface in C#?

I am pretty new in C# (I came from Java) and I have a doubt about the use of:

I have this class:

namespace DataModel.MaliciousCode
{

    public class PagedMalicious : Shared.Paged
    {
        public IEnumerable<MaliciousSmall> MaliciousCode { get; set; }

    }
}

As you can see this class contains only a IEnumerable<MaliciousSmall> MaliciousCode

Reading the online documentation it seems to me to understand that IEnumerable is an interface that give me an iterator on a non generic collection.

What exactly means the previous assertion?

I have MaliciousSmall that is the type of a model object in my application (an object that contains some properties that map the fields of a table on the DB)

So my doubt are:

  • IEnumerable<MaliciousSmall> MaliciousCode : MaliciousCode is an iterable collection of MaliciousSmall objects? So it means that it represent a collection and some provided methods to iterate on it?

  • If the previous assertion is true, ok MaliciousCode object is an iterable collection but IEnumerable is an interface so who implement the method to iterate on this collection (coming from Java I think that an interface is not provided of implemented methods)

Some one can help me to understand these things?

Tnx

Andrea

IEnumerable<MaliciousSmall> MaliciousCode : MaliciousCode is an iterable collection of MaliciousSmall objects? So it means that it represent a collection and some provided methods to iterate on it?

Sort of - IEnumerable<T> provides one method - GetEnumerator - which returns an IEnumerator<T> . THAT interface allows you to iterate over the collection. Pre-Linq all IEnumerable allowed you to do was use the collection in a foreach loop (or use the provided IEnumerator directly, which is rare). Linq has since defined extension methods on IEumerable<T> that allow more sophisticated queries like Select , Where , Count , etc.

If the previous assertion is true, ok MaliciousCode object is an iterable collection but IEnumerable is an interface so who implement the method to iterate on this collection (coming from Java I think that an interface is not provided of implemented methods)

Typically the implementation is provided by using an underlying collection type like List<MaliciousSmall> or MaliciousSmall[] . So the IEnumerable implementation is provided by that class. The yield keyword introduced in C# 2.0 allows you to "return" an IEnumerable<T> and let the compiler provide the actual implementation.

So in your class, you might internally implement the collection as a List<T> :

public class PagedMalicious : Shared.Paged
{
    public IEnumerable<MaliciousSmall> MaliciousCode { get; set; }

    public PagedMalicious()
    {
        MaliciousCode = new List<MaliciousSmall>();
    }

    // other private methods that add to MaliciousCode
}

The use of IEnumerable<T> allows you to change the internal implementation without changing the public interface.

Your property MaliciousCode , represents an object of a class that implements IEnumerable<T> interface. On it's own, IEnumerable does not really mean anything. It just provides a structure. It is user's responsibility to implement the methods that are provided with interface whatever way the see it suitable.

Edit: Here is a simple example to demonstrate:

private void Form3_Load(object sender, EventArgs e)
    {
        Parent parent = new Parent();
        parent.Child = new List<Child>(); // -> this is where implementer is decided.  
        //Before this line, Child property is not instantiated and is not referring to any object.
    }

    public class Parent
    {
        public IEnumerable<Child> Child { get; set; }
    }

    public class Child
    {
        public int MyProperty { get; set; }
    } 

To your doubts: Yes and Yes The interface itself can't implement anything, but you can assign an array of MaliciousSmall or a List<MaliciousSmall>. Both of them implement IEnumerable<MaliciousSmall>

IEnumerable<T> is the equivalent of Java's Iterable<T> . Since the early versions of C# did not have generics, IEnumerable was the only iterator available at that time. You can think of it as a kind of IEnumerable<object> .

Most generic collection types implement IEnumerable<T> , including arrays. The generic variant requires the non-generic variant to be implemented, therefore most collections (generic or not) implement IEnumerable .

However, these iterators are not limited to represent collections. They provide methods that allow you to enumerate items and these methods can generate items algorithmically as well. For instance an - in theory - endless enumeration of square numbers could be provided by an enumeration without the requirement to store those numbers anywhere.

In your case the IEnumerable<MaliciousSmall> MaliciousCode property could yield MaliciousSmall objects from the DB one by one as the enumeration is being enumerated without storing them in a collection object first.

Implementing IEnumerable<T> yourself requires the implementation of the IEnumerator<T> GetEnumerator() method. It returns an enumerator object which requires the implementation of the methods bool MoveNext() , void Dispose() , void Reset() and the property T Current { get; } T Current { get; } .

You can implement these interfaces the good old way, by writing a lot of code, or you can use C#'s iterators. Iterators use a lot of compiler magic to create enumerables and enumerators automatically behind the scenes. See: Iterators (C# and Visual Basic) .


As an example of C# iterators, let's implement your example with them (I dropped the setter as it stands in the way here):

public class PagedMalicious : Shared.Paged
{
    public IEnumerable<MaliciousSmall> MaliciousCode
    {
        get
        {
            using (var conn = new SqlConnection("<my server connection>")) {
                var cmd = new SqlCommand("SELECT name, number FROM myTable", conn);
                conn.Open();
                using (var reader = cmd.ExecuteReader()) {
                    while (reader.Read()) {
                        var maliciousSmall = new MaliciousSmall {
                            Name = reader.GetString(0), 
                            Number = reader.GetInt32(1) 
                        };
                        yield return maliciousSmall;
                    }
                }
            }
        }
    }
}

Each time yield return is executed, the control is passed back to the caller and he gets the next item. The state of the getter method is kept intact and its execution is halted here until the caller continues iterating and requires the next item. When he does so, the execution resumes just after the yield return statement.

You can see from this example, that enumerations are evaluated in a lazy way. The following code sums up the numbers of the whole table. The items are never stored in a collection; they are retrieved from the DB and created as they are enumerated. This is an advantage, if you have one million records! (You would use an SQL SUM aggregate function in a productive piece of code, so.)

var pagedMalicious = new PagedMalicious();
int sum = 0;
foreach (MaliciousSmall item in pagedMalicious.MaliciousCode) {
    sum += item.Number;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM