Web Log di Adrian Florea

"You know you've achieved perfection in design, not when you have nothing more to add, but when you have nothing more to take away." Antoine de Saint-Exupery
posts - 440, comments - 2715, trackbacks - 3944

My Links

Archives

Post Categories

Image Galleries

.RO Blogs

.RO People

.RO Sites

Blogs

Furls

Links

vinCitori

Sul cast del foreach

Nel suo post di ieri, "foreach l'insidioso", Luca si e' chiesto come mai il seguente snippet:

using System.Collections.Generic;

interface IPersistent { }
class Invoice : IPersistent { }
class Order : IPersistent { }

class Program {
    static void Main() {
        List<IPersistent> changedDocuments = new List<IPersistent>();
        changedDocuments.Add(new Invoice());
        changedDocuments.Add(new Order());
        foreach (Invoice changedInvoice in changedDocuments) { }
    }
}

compili. Secondo me, il comportamento del compilatore e' giusto, voluto e documentato. Le specifiche del linguaggio (15.8.4, ECMA-334), dicono:

"A foreach statement of the form

foreach(V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

The variable e is not visible to or accessible to the expression x or the embedded statement or any other source code of the program. The variable v is read-only in the embedded statement. If there is not an explicit conversion from T (the element type) to V (the type in the foreach statement), an error is produced and no further steps are taken."

Quindi e' la responsabilita' del programmatore quella di assicurarsi che il cast sia valido. Potremmo volere per esempio fare un foreach su tutte le Label nella ControlCollection dei Controls di una Form - sara' il nostro compito quello di "filtrare" gli elementi di tipo Label e fare il foreach soltanto su questi, oppure assicurarsi che nella Controls ci sono solo Label. Perche' il compilatore non ci aiuta? Probabilmente perche' la lista potrebbe essere costruita anche altrove!

In ogni caso, se cerchiamo un aiuto da parte dell'intellisense e del compilatore, possiamo sostituire il foreach nello snippet di Luca, con:

changedDocuments.ForEach(delegate(IPersistent persistent) { });

Sorprendemente, otteniamo anche un codice IL piu' piccolo rispetto alla variante con foreach. Il type parameter dell'argomento action del metodo ForEach, e' vincolato ora ad essere identico al type parameter della nostra List, cioe' IPersistent.

Print | posted on mercoledì 14 gennaio 2009 12:11 | Filed Under [ Carillon .NET ]

Powered by:
Powered By Subtext Powered By ASP.NET