Interesting moments in C # (foreach)

    In this article, we briefly go over the features of foreach. You most likely know the first moment, you most likely do not know the second moment.

    Previous article about Array

    First moment


    At interviews they often ask - “What needs to be done to make your class work with foreach?”. The answer to this question usually sounds like this - "Implement IEnumerable." This answer is correct, but not complete. In principle, this answer at the interview is enough and I have never met anyone to consider it wrong. In fact, it foreachuses duck typing . In order for our class to work in, it is foreachenough to have a method that GetEnumeratorreturns something that has a method MoveNextand property Current.

    Remembering these methods is not necessary if you slip your wrong class into the foreachcompiler honestly tells you what exactly is missing in this class.

    Examples

    Test foreach:
    class Program
    {
    	static void Main(string[] args)
    	{
    		var container = new Container();
    		foreach (var item in container)
    		{
    		}
    	}
    }
    

    Wrong container:
    public class Container
    {
    }
    

    Compiler Error:
    foreach statement cannot operate on variables of type 'Container' because 'Container' does not contain a public definition for 'GetEnumerator'

    Add a method GetEnumeratorto the container and enumerator class.

    The correct container:
    public class Container
    {
    	public Enumerator GetEnumerator()
    	{
    		return new Enumerator();
    	}
    }
    

    Incorrect enumerator:
    public class Enumerator
    {
    }
    

    Compiler Error:
    foreach requires that the return type 'Enumerator' of 'Container.GetEnumerator()' must have a suitable public MoveNext method and public Current property

    Add a method MoveNextand property Currentto the enumerator.

    The correct enumerator:
    public class Enumerator
    {
    	public bool MoveNext()
    	{
    		return false;
    	}
    	public object Current
    	{
    		get { return null; }
    	}
    }
    


    Now the compiler is fine.

    Note:
    The property Currentcan return any type as ref typeand so value type. Actually, this was the reason for the use of "duck typing", at a time when it was not generics, to avoid unnecessary boxingand unboxing.

    Second moment


    At the interview there are questions about IDisposableand in addition to general questions about manual resource management, there is a question about when the compiler can automatically call a method Dispose. We all know the answer - it is Disposecalled automatically when using the operator using(). This answer is correct, but incomplete! A method Disposecan be called in two cases, in addition using(), it is called in foreachfor the enumerator, if the enumerator implements IDisposable.

    Examples

    Enumerator with Dispose:
    using System;
    public class Enumerator : IDisposable
    {
    	public bool MoveNext()
    	{
    		return false;
    	}
    	public object Current
    	{
    		get { return null; }
    	}
    	public void Dispose()
    	{
    		Console.WriteLine("Dispose");
    	}
    }
    


    Now when we run the example, we will see the “Dispose” line in the console.

    For those who are interested, here is the code that the compiler generates for our case:
    Container container = new Container();
    Enumerator enumerator = container.GetEnumerator();
    try
    {
    	while (enumerator.MoveNext())
    	{
    		var element = enumerator.Current;
    		// содержимое foreach
    	}
    }
    finally
    {
    	IDisposable disposable = enumerator as IDisposable;
    	if (disposable != null)
    		disposable.Dispose();
    }
    

    Thank you all for your attention!

    Also popular now: