Perfil de GyörgyBalássy GyörgyFotosBlogListasMás Herramientas Ayuda

Blog


    27 julio

    Minek annyi foreach?

    Ha elfogadjuk azt az alaptételt, hogy “kevesebb kód – kevesebb bug”, akkor miért ragaszkodunk annyira a szószátyár foreach ciklusokhoz?

    Számtalanszor látok olyan kódot, ami egy lista minden elemével csinál valamit, gyakran csak átadja egy metódusnak:

      List<string> vezerek = new List<string> { "Álmos", "Előd", "Ond", "Kond", "Tas", "Huba", "Töhötöm" };
      foreach( var vezer in vezerek )
      {
        Console.WriteLine( vezer );
      }

    Ennél az alábbi sokkal egyszerűbb és pont ugyanezt csinálja:

      vezerek.ForEach( vezer => Console.WriteLine( vezer ) );

    Persze olyan is van, amikor nem minden elemmel akarjuk elvégezni ezt a műveletet, csak azokkal, ami teljesít egy feltételt:

      foreach( var vezer in vezerek )
      {
        if( vezer.EndsWith( "d" ) )
        {
          Console.WriteLine( vezer );
        }
      }

    Ennél is van egyszerűbb:

      vezerek.FindAll( vezer => vezer.EndsWith( "d" ) ).ForEach( vezer => Console.WriteLine( vezer ) );

    Az első példában egyértelmű, hogy a rövidebb megoldást célszerű alkalmazni, mert pont ugyanazt csinálja a két kód, csak az egyik rövidebb és jobban olvasható. A második esetben azonban a FindAll és a ForEach egyaránt egy-egy for ciklusra fordul, tehát itt már két ciklus fog lefutni egymás után – azaz  mérlegelnünk kell az olvashatóság és a teljesítmény között. Például nem biztos, hogy ez a legjobb megoldás egy CheckBoxList kiválasztott elemeinek DataTable-be töltésére:

      DataTable dt = new DataTable();
      dt.Columns.Add( "Item", typeof( string ) );
      this.cblNames.Items
    .Cast<ListItem>()
    .ToList()
    .FindAll( item => item.Selected )
    .ForEach( item => dt.Rows.Add( item.Value ) );

    Ha eltekintünk a teljesítménytől, akkor is fontos szempont, hogy sokak számára a fenti lambdás írásmód még nem számít “olvashatónak”, ugyanúgy ahogy a (feltétel) ? (ha igaz) : (ha hamis) szintakszis (van ennek valami szép neve?) sem. Mindkettő túl tömör. Azonban van egy óriási előnyük: ordít róluk, hogy mire szolgál az adott kódsor. A foreach csak egy ciklus, a ForEach pedig egy gyűjtemény minden elemével elvégez egy műveletet. Az if csak egy feltétel vizsgálat, a (feltétel) ? (ha igaz) : (ha hamis) viszont egy feltételtől függő értékadás. Kétségkívül kell hozzá kis gyakorlat, de megéri.

    Ti mit gondoltok, olvashatóbb a tömörebb kód?

    Technorati-címkék: ,

    Comentarios (9)

    Espera...
    El comentario que has escrito es demasiado largo. Acórtalo.
    No has escrito nada. Vuelve a intentarlo.
    No se puede agregar tu comentario en este momento. Vuelve a intentarlo más tarde.
    Para agregar un comentario, necesitas permiso de tus padres. Pedir permiso
    Tus padres han desactivado los comentarios.
    No se puede eliminar tu comentario en este momento. Vuelve a intentarlo más tarde.
    Has superado el número máximo de comentarios que se puede dejar en un día. Vuelve a intentarlo en 24 horas.
    Se ha deshabilitado la capacidad de tu cuenta de dejar comentarios porque nuestros sistemas indican que podrías estar enviando correo no solicitado a otros usuarios. Si crees que tu cuenta se ha deshabilitado por error, ponte en contacto con el servicio de soporte técnico de Windows Live.
    Para terminar de dejar tu comentario, realiza la siguiente comprobación de seguridad.
    Los caracteres que escribas en la comprobación de seguridad deben coincidir con los de la imagen o el audio.

    Para agregar un comentario, inicia sesión con tu cuenta de Windows Live ID (si utilizas Hotmail, Messenger o Xbox LIVE, ya tienes una cuenta de Windows Live ID). Iniciar sesión


    ¿No tienes una cuenta de Windows Live ID? Regístrate

    György Balássyescribió:
    Chikk, ebben teljesen igazad van, ezt kifelejtettem a cikkből. Köszi!
    30 Julio
    Azé beteg ez a lamda forícs, mert ott bizony egy delegate hívás fog fordítódni, ami sokkal lassabb ebben a példában mint a sima foreach. Próbáljátok ki!
    29 Julio
    Balázs Misángyiescribió:
    Nálunk az a főszabály, hogy a fenti kiterjesztési műveleteket olyankor illik használni, ha a listát elemenként dolgozzuk fel, azaz ha nincs a műveleteknek mellékhatása. Ha már valami komoly változást okozunk a külvilágban (pl. stream-re írunk, adatbázisba mentünk, kivételt dobálunk, stb), akkor illik az imperatív szerkezetek használata. Így nemcsak hogy kihasználjuk a kényelmes, új szerkezeteket, ráadásul még az is eldönthető ránézésre, hogy az adott kód valaminek a kiszámítása, vagy végrehajtása lesz-e.
    28 Julio
    Peter Juhaszescribió:
    Én most azt nem értem ,hogy a 4-es Frameworkben a ForEach miért nem egy extension method az IEnumerable<T>-hez. Miért csak kizárólag a List<T> tudja ezt (még nem is az IList<T>)?
    28 Julio
    György Balássyescribió:
    Péter: Látom rákattantál a code contracts-re :) Egyébként erre a feltételes ForEach-re én is eljutottam, és nagyon nem értem, hogy miért nincs benne a Frameworkben.
    28 Julio
    Peter Juhaszescribió:
    Megoldás a feltételes ForEach-re:

    public static void ForEach<T>(this IEnumerable<T> enumerable, Predicate<T> predicate, Action<T> action)
    {
    Contract.Requires(enumerable != null);
    Contract.Requires(predicate != null);
    Contract.Requires(action != null);

    foreach (T item in enumerable)
    {
    if (predicate(item))
    action(item);
    }
    }
    27 Julio
    Csaba Balázsescribió:
    A rövidebb kód olvashatóbb, de a 2-3 soros foreachnél nem sokkal rövidebb a lambda expressionös formátum. Ilyenkor viszont az dönt, hogy ki hogy látja át a kódot, azaz mennyire olyan, mint amit ő szokott írni, azaz mennyire elterjedt a használata.

    Sokszor mi is a ? : operátort használjuk, sőt van, amikor ? : ? : -ot, azaz az A?B:C-nél a C megint ?:-ba fordul. Rövid, tömör, de mégis annyi benne az operátor, hogy van, amikor if else if-et csinálunk belőle.

    A forach és List<>.ForEach viszont előrelépés a régi Delphi-s programozók for ciklusaihoz, ott 6-7 sor -> 1-2 sor már olvashatóságot növel.
    27 Julio
    Attila Érsekescribió:
    Közben végülis rájöttem hogy én vagyok a hülye, és a Cast is tökéletes erre a célra :)
    27 Julio
    Attila Érsekescribió:
    Általában olvashatóbb, feltéve, hogy a fejlesztő (olvasó) számára rutinból ismert az adott szintaktika.
    Egy dologot azért nem tud (legalábbis szépen) a lambda: a típuskényszerítést. Példa:
    class A {}
    class B : A {}
    class C { public IEnumerable<A> AList; }

    foreach (B b in c.AList) //explicit típus kényszerítés, feltételezi, hogy AList minden eleme B típusú
    {
    }

    ez most durva példa de a régi Hashtable és társainál (object típusú elemek enumerációi) igen hasznos!
    Szerintem ez kifejezésben leírva már nem olyan szép (és a mögöttes IL kódra is kiváncsi lennék):
    c.AList.Select(a => (B)a).Where(b => b.IsTrue).ForEach(b => Debug.WriteLine(b));
    27 Julio

    Vínculos de referencia

    La dirección URL del vínculo de referencia de esta entrada es:
    http://balassy.spaces.live.com/blog/cns!8742D9F327180554!1268.trak
    Weblogs que hacen referencia a esta entrada
    • Ninguno