Var (II).Los hermanos de Simba

Este post viene como consecuencia de los comentarios del gran @eiximenis, en este otro var en c# si, o var no. Lo que ha hecho Eduard es elevar con la segunda respuesta la entrada al destino de los elegidos:).

Referente al titulo “Los hermanos de Simba” es porque la fiera se va a comer al gatito.

La primera de las respuestas, he de reconocer que estuve pensando en escribirla en el primero de los post, pero eso de la vida que te hace perezoso y en un momento decides cortar.

Eduard nos dice lo siguiente.

Efectivamente, como bien dices en var el tipo del lvalue es siempre igual al del rvalue (eso es var precisamente). Pero, en según que ocasiones, eso no nos sirve… Un ejemplo rápido…

myListbox.Items.Add(new Foo());

foreach (var fooItem in myListbox.Items) { … }

En este caso, al usar var, fooItem es un Object, pero si yo sé que en la lista hay solo Foo, probablemente querré que fooItem sea de tipo Foo. Este es solo un ejemplo. A eso me refería yo 😉

Por que ocurre esto? Porque en el ejemplo Eduard está utilizando un ObjectCollection, claro a alguien le pareció demasiado tedioso con la aparición de generic reescribir Windows Form y demás y lo que se hizo fue dejar la cosa como estaba e implementar IEnumerable<T>, todos sabemos que para utilizar el magnifico foreach alguien debe de implementar en sus clases como mínimo IEnumerable y digo como mínimo, puesto que lo lógico es utilizar IEnumerable<T>.

Vamos con un ejemplo, partimos de la siguiente clase.

   1: public class Foo

   2: {  

   3:    

   4: }

Si yo escribo el siguiente conjunto de instrucciones

   1: var foo = new Foo();

   2: foreach (var item in foo)

   3: {

   4:  

   5: }

Recibo un error de compilación donde me dice lo siguiente.

La instrucción foreach no puede funcionar en variables de tipo ‘Namespace.Foo’ porque ‘Namespace.Foo’ no contiene ninguna definición pública para ‘GetEnumerator’.

Sí Implementamos IEnumerable en “Foo”.

   1: public class Foo:System.Collections.IEnumerable

   2: {

   3:  

   4:     public System.Collections.IEnumerator GetEnumerator()

   5:     {

   6:         throw new NotImplementedException();

   7:     }

   8: }

A partir de este momento yo puedo ejecutar foreach in “Foo”.

Pero claro, con esto no contestamos a Eduard, para ello nos debemos fijar en el valor devuelto por GetEnumerator un IEnumerator cuya definición es la siguiente.

   1: [ComVisible(true), Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]

   2: public interface IEnumerator

   3: {

   4:     bool MoveNext();

   5:     object Current { get; }

   6:     void Reset();

   7: }

   8:  

Si nos fijamos Current es object, por eso es por lo que a “var” se le asigna un “object”, con lo cual “var” si está haciendo lo que dice hacer.

Claro porque “var” si funciona con List<T>,etc,etc, pues sencillo porque estos han subido un nivel y han implementado IEnumerable<T> y por tanto IEnumerator<T> donde la  propiedad Current devuelve T.

   1: public interface IEnumerator<T> : IDisposable, IEnumerator

   2: {

   3:     // Properties

   4:     T Current { get; }

   5: }

Pero como se que me va a decir que porque yo puede hacer lo siguiente.

   1: private void Metodo()

   2: {

   3:     var Lista = new System.Collections.ArrayList();

   4:     Lista.Add(new Foo());

   5:     foreach (Foo item in Lista)

   6:     {

   7:  

   8:     }

   9: }

Le vamos a contestar, con un simple “una cosa es lo que tus ojos ven y otra bien distinta lo que hay”.

Un poquito de IL:).

   1: .method private hidebysig instance void Metodo() cil managed

   2: {

   3:     .maxstack 2

   4:     .locals init (

   5:         [0] class [mscorlib]System.Collections.ArrayList Lista,

   6:         [1] class WindowsFormsApplication24.Foo item,

   7:         [2] class [mscorlib]System.Collections.IEnumerator CS$5$0000,

   8:         [3] bool CS$4$0001,

   9:         [4] class [mscorlib]System.IDisposable CS$0$0002)

  10:     L_0000: nop 

  11:     L_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()

  12:     L_0006: stloc.0 

  13:     L_0007: ldloc.0 

  14:     L_0008: newobj instance void WindowsFormsApplication24.Foo::.ctor()

  15:     L_000d: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)

  16:     L_0012: pop 

  17:     L_0013: nop 

  18:     L_0014: ldloc.0 

  19:     L_0015: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.ArrayList::GetEnumerator()

  20:     L_001a: stloc.2 

  21:     L_001b: br.s L_002b

  22:     L_001d: ldloc.2 

  23:     L_001e: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()

  24:     L_0023: castclass WindowsFormsApplication24.Foo

  25:     L_0028: stloc.1 

  26:     L_0029: nop 

  27:     L_002a: nop 

  28:     L_002b: ldloc.2 

  29:     L_002c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()

  30:     L_0031: stloc.3 

  31:     L_0032: ldloc.3 

  32:     L_0033: brtrue.s L_001d

  33:     L_0035: leave.s L_0051

  34:     L_0037: ldloc.2 

  35:     L_0038: isinst [mscorlib]System.IDisposable

  36:     L_003d: stloc.s CS$0$0002

  37:     L_003f: ldloc.s CS$0$0002

  38:     L_0041: ldnull 

  39:     L_0042: ceq 

  40:     L_0044: stloc.3 

  41:     L_0045: ldloc.3 

  42:     L_0046: brtrue.s L_0050

  43:     L_0048: ldloc.s CS$0$0002

  44:     L_004a: callvirt instance void [mscorlib]System.IDisposable::Dispose()

  45:     L_004f: nop 

  46:     L_0050: endfinally 

  47:     L_0051: nop 

  48:     L_0052: ret 

  49:     .try L_001b to L_0037 finally handler L_0037 to L_0051

  50: }

y más concretamente nos vamos a centrar en estás dos instrucciones

   1: L_001e: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()

   2: L_0023: castclass WindowsFormsApplication24.Foo

Hombre un OpCodes.Castclass. Lo más bonito de este link no es otra cosa que la siguiente frase.

“Intenta convertir un objeto pasado por referencia en la clase especificada”

Efectivamente “intenta”, con lo cual si no puede, sorpresa y nos llevaremos como premio, un magnifico “InvalidCastException” y para ello simplemente creamos una clase Foo1 y la agregamos al ArrayList.

   1: private void Metodo()

   2: {

   3:  var Lista = new System.Collections.ArrayList();

   4:  Lista.Add(new Foo());

   5:  Lista.Add(new Foo1());

   6:  foreach (Foo item in Lista)

   7:  {

   8:  

   9:  }

  10: }

Lo que es evidente, que var funciona, puesto que es imposible saber que es lo que ArrayList tiene. Solo sabemos que tiene object y por tanto “var” es “object”.

Pero se te olvido una cosa OfType<T>() y entonces “var” si que funciona perfectamente, aunque que quede claro que no aconsejo el uso de esta instrucción en este caso, puesto que nos comemos un elemento. Lo que si aconsejo es que nos olvidemos para siempre si puede ser de las colecciones no genericas, aunque algunas veces es imposible y más cuando se trata de collecction ligadas a WindowsForm,WPF,Silverlight, que bastante mal se llevan con el uso de Generic.

En definitiva para que “var” funcione correctamente en este caso basta con escribir lo siguiente.

   1: private void Metodo()

   2: {

   3:     var Lista = new System.Collections.ArrayList();

   4:     Lista.Add(new Foo());

   5:     Lista.Add(new Foo1());

   6:     foreach (var item in Lista.OfType<Foo>())

   7:     {

   8:  

   9:     }

  10: }

Aunque como dije anteriormente el segundo elemento nos lo comemos, de esta forma veo que yo si utilizaría “var” siempre en un foreach.

Cuando me refería a no abusar,  me refería, aunque se puede, a utilizar este tipo de cosas.

   1: var i = ((Func<int, int, int>)((x, y) => x + y))(10,10);

Esto me recuerda a un antiguo compañero que cuando sumaba enteros escribía “var z = 0x3e8 + 0x3e8;” que si hombre que sí que z = 2000 ó z = 1000+ 1000;

Aclarado el primer comentario, vamos con el segundo que ese si que está lleno de mala leche:).

La pregunta es… Suponiendo que lo anterior compilase, esa otra línea debería hacerlo?

var r = (x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17 => x1+x17)(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);

Repito: si la sentencia anterior compilase, esa debería compilar también? Sí? No? Tic, tac…

PD: Si, admito que si alguien crea una lambda con 17 parámetros es para darle una colleja 😛

La respuesta es No y os explico, para ello nos debemos trasladar a la primera aparición de Func y Action en el Framework 3.5. El número maximo de parametros permitidos eran 4.

Dibujo

Si avanzamos en el tiempo y analizamos esto mismo a partir del Framework 4.0 nos encontramos con que los parametros permitidos son 16, fijaos en la maldad de poner 17:).

Dibujo

tic,tac…. Pero no conformándonos con esto vamos a demostrarle a Eduard que si  compilo.

Alguien me impide a mí definir un Func y un Action con 17 parámetros o con 50, pues no otra cosa es que directamente te pueden llamar “ñapas”:).

   1: namespace System

   2: {

   3:     public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16,in T17, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16,T17 arg17); 

   4:  

   5: }

Y ahora vamos a hacer que compile utilizando “var”.

   1: var i = ((Func<int, int, int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int>)

   2:                ((x1, x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17) => 

   3:                  x1+x2+x1+x4+x5+x6+x7+x8+x9+x10+x11+x12+x13+x14+x15+x16+x17))

   4:                (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);

   5: Console.Write(i);

El resultado obtenido que es pues sencillo 151, pero claro no debéis de utilizar esta técnica para sumar una secuencia de números correlativa, mas vale que  veáis este video.

http://www.youtube.com/watch?feature=player_embedded&list=PLB33C2EAFBCB22655&v=9gv2YyRvdB0

Tic,Tac……

Saludos,

14 comentarios sobre “Var (II).Los hermanos de Simba”

  1. 😉
    Genial Pedro, simplemente genial!

    El ejemplo de la ObjectCollection lo has explicado cojonudamente. Ojo, que yo NO digo que var no haga lo que toca. Evidentemente que lo hace. Solo digo que, por norma general, este es un caso en el que NO se puede usar (puede == no es lo que quieres hacer). Por supuesto usando OfType o usando Cast (si sabes que todos los elementos son del tipo T) ya está arreglado y puedes usar var sin problemas.
    Hay veces en que uno quiere que el tipo del lvalue sea distinto del rvalue. En esos casos no se puede usar var. O si quieres usar var debes usar un casting para convertir el tipo del rvalue al tipo deseado.

    Y ahora vayamos a la lambda…
    Con la lambda de 17 parámetros simplemente quería demostrar que las funciones NO son ciudadanos de primer nivel en C#.
    Claro, si tu te declaras tu Action con 17 parámetros te funciona sí… Pero en un lenguaje donde verdaderamente las funciones fuesen «ciudadanos de primer nivel» eso no sería necesario 🙂

    En fin, que vaya dos posts te has marcado… He disfrutado como un enano (¿o debería decir un gatito?) leyéndolos…
    Ahora es el turno de Jorge 😉

    Saludos!

    PD: De donde viene la expresión «¿disfrutar como un enano?» Será de la película de Blancanieves y los siete enanitos viciosos??? xDDDDDDD

  2. Os voy a matar a ambos y si se cruza Jorge también le cae una … el objetivo principal de muchos de los cambios y mejoras en la SINTAXIS/IDE/COMPILADOR (por debajo las tripas siguen siendo iguales) es lograr una mejor productividad, un código más limpio, etc.

    Ahora la crítica: los ejemplos donde muestran/demuestran que «var» puede fallar, tienen un problema de base >> «BAD SMELL», es código que al momento de mirarlo, ya huele mal; ya sabes que hay algo mal por ahí detrás.

    Es un poco subjetivo lo que comento, pero la experiencia y el sentido común, hacen que cuando tiramos líneas lo hagamos de forma tal que luego podamos entender lo que escribimos, que cumpla el objetivo con el que lo escribimos y que además sea bonito 🙂

    Un lambda de 17 parametros? te corto un dedo !!! una colexxxion con elementos indistintos que se enumera asumiendo un mismo tipo, pues no estas entendiendo el negocio o la has cagado de colores … sigo?

    Yo creo que «var» cumple y con creces el hecho de ayudarnos a escribir mejor código … 😀 lo 2do sigue siendo lo más importante

    Saludos

  3. @Bruno
    Desulfúrate hombre… xDDDD

    Recuerda una cosa, lo he dicho varias veces ya: yo pienso que var debería ser obligatorio. Hace tiempo expuse mis razones, y como he dicho x twitter estoy afilando el hacha para cuando Jorge esgrima sus razones en contra de var.

    Dicho esto, simplemente:

    1. Iterar sobre una ObjectCollection ERA UN EJEMPLO de cuando no se puede usar var. Que ObjectCollection es un truño? Puede, pero es un truño de .NET 🙂
    2. La lambda de 17 parámetros, que tan rápidamente ha cazado Pedro, era un ejemplo de que las funciones NO son en C# ciudadanos de primer nivel.

    Y no hay nada más. Nos vemos en los vares. Y en los bares. 😛

  4. Buen artículo,

    Me gustaria comentar que en WPF, trabajando con MVVM todas las colecciones te las haces tu (en concreto lo normal es utilizar ObservableCollection), por lo que no usar generics es para despedir a alguien.

  5. @Eduard,@Xavi Paper gracias por los comentarios.

    @Joan totalmente de acuerdo y sobre todo sino se utiliza MVVM, aunque el ObservableCollection es de las que tampoco tiene desperdicio:)

    @Bruno lo tuyo se merece otro comentario:)

  6. Hola @Bruno, este es para ti y todo tuyo.

    >>Os voy a matar a ambos y si se cruza Jorge también le cae una … el objetivo principal de muchos de los cambios y mejoras  en la SINTAXIS/IDE/COMPILADOR >>(por debajo las tripas siguen siendo iguales) es lograr una mejor productividad, un código más limpio, etc.

    Hola Yo creo lo siguiente, que var fue inventada por un tester.

    Sabes cuando? Cuando se dio cuenta que ciertas devoluciones derivadas del uso de linq

    eran totalmente inmanejables en la parte izquierda. Es decir no se podían declarar.

    Te pongo un ejemplo, siguiendo con la clase Foo.

    List<Foo> Lista = new List<Foo>();

    var resultado = from b in Lista

                   group b by new { b.Id, b.Nombre } into g

                   select new { Id = g.Key.Id };

    Imagino que te has parado a pensar alguna vez que deberías declarar en la parte izquierda?, yo te respondo,algo así

    System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Linq.IGrouping`2[<>f__AnonymousType0`2[System.Int32,System.String],WindowsFormsApplication24.Foo],<>f__AnonymousType1`1[System.Int32]]

    Con lo cual esa tarde se te caen los dedos.En definitiva esta persona puso sus quejas que no deberían ser pocas y por la noche alguien se puso a ver como solucionaba el problema y de hay nace var.

    >>Ahora la crítica: los ejemplos donde muestran/demuestran que «var» puede fallar, tienen un problema de base >> «BAD SMELL», es código que al momento de >>mirarlo, ya huele mal; ya sabes que hay algo mal por ahí detrás.

    >>Es un poco subjetivo lo que comento, pero la experiencia y el sentido común, hacen que cuando tiramos líneas lo hagamos de forma tal que luego podamos >>entender lo que escribimos, que cumpla el objetivo con el que lo escribimos y que además sea bonito 🙂

    En estos dos ejemplos no he hecho más que contestar a las dos respuestas de Eduard a esta otra entrada

    geeks.ms/…/var-en-c-si-o-var-no.aspx

    Donde como puedes observar apoyo incondicionalmente el uso de var, en eso coincidimos.

    Pero me hace gracia que hables de «BAD SMELL». Ahora te pregunto yo es «BAD SMELL»?.

    1.Xaml.

    2.Todas las colecciones asociados a los controles.

    3.Si me traslado en el tiempo no poder hacer esto,porque el ide no lo permite, aunque trabaja perfectamente en ejecución.

    public class Form:FormBase<foo>

    {

    }

    4.Los Config.

    Sin ir mas lejos y utilizando las mas modernas tecnologias MVC Y Azure, no te equivoques en esto

    <runtime>

       <assemblyBinding xmlns=»urn:schemas-microsoft-com:asm.v1″>

         <dependentAssembly>      

           <assemblyIdentity name=»System.Web.Mvc» publicKeyToken=»31bf3856ad364e35″ />

           <bindingRedirect oldVersion=»0.0.0.0-2.0.0.0″ newVersion=»3.0.0.0″ />

         </dependentAssembly>

       </assemblyBinding>

     </runtime>

    porque yo me equivoque y en vez de escribir «3.0.0.0» escribi esto «3.0.0.0 » y estuve no te cuento ni el tiempo.

    5.En definitiva todo lo derivado del api que se llamo Framework 3.0.

    6.O que una lambda pueda ser asignada a un Expression o a Func,Action,Predicate,etc…

    Esto si que es «BAD SMELL» y la verdad es que te veo poco protectar.

    >>Un lambda de 17 parametros? te corto un dedo !!! una colexxxion con elementos indistintos que se enumera asumiendo un mismo tipo, pues no estas entendiendo >>el negocio o la has cagado de colores … sigo?

    No sigas, voy a continuar yo. En la vida he utilizado un código como ese y espero no hacerlo, simplemente te digo que lo que he hecho es responder a Eduard y explicar creo que bien porque var no funciona con las collecciones de todos los controles y lo repito, todos los controles:)

    ListBox,

    GridView,

    ComboBox,

    etc,etc,etc.

    Respecto a cortarme el dedo, deberías cortarselo primero al que invento la de 16 parametros.

    Sabes donde?

    se utiliza en

    System.Data.Objects.CompiledQuery.

    Jo si es algo recomendado a no parar por el equipo de EF para optimizar las query de linq.

    >>Yo creo que «var» cumple y con creces el hecho de ayudarnos a escribir mejor código … 😀 lo 2do sigue siendo lo más importante

    Efectivamente y en ningún momento he dicho lo contrario.

    Ves como si me entero y no la he cagado en colores.

    Así que vamonos de vares:)

    Saludos

  7. @Pedro, con el pedazo de respuesta que me has dado (q crack!), te debo mínimo un comentario (o un pòst, pero no me gusta hablar de estas cosas en mi blog). Vamos por partes, efectivamente VAR también ayuda a que no veamos en nuestro código definiciones de tipos de 400 chars, pero ayuda de la misma forma en que C# y VB.Net ayudan a que no veamos el código compilado, ¿no?. Ese es mi punto, es una mejora/evolución del lenguaje que nos ayuda a ser más productivos (lo demás son matices lo siento, soy pobre de luces).

    Ahora bien, BAD SMELL en archivos de configuración o XAML? pues mi limitado cerebro no me da para tanto, lo siento. Seguramente alguien lo podrá ver, pero yo solo puedo y poco en C#, los demás ejemplos ni los veo Por cierto: la referencia correcta es «CODE SMELL» ves como soy cortico 😉 (http://en.wikipedia.org/wiki/Code_smell)

    Finalmente, te has tomado el tiempo de refutar mis párrafos uno a uno, inclusive has esquivado el hachazo para cortar el dedo y se lo has enviado al pobre dude que se picó las CompiledQueries !!! Todo por definir si VAR o no VAR, este es el motivo por el que no hablo de esto en mi blog 😉

    Saludos

  8. Lo bueno es que estamos teniendo esta discusión 3 tios que estamos A FAVOR de var…

    Cuando entre Jorge, aquí se va a liar parda!! 😛

    Saludos y cervezas!

  9. @Bruno,

    Lo primero es aclarar que no me considero un «crack» y tampoco a ti un «cortico», es más a ti yo si te considero un «crack».

    Pero quiero que te des cuenta de una cosa coincido en el uso de VAR y más claro no lo he podido decir.

    Respecto a la aclaración de «BAD SMELL» y «CODE SMELL», gracias por enseñarnos. Ahora me vas a permitir una aclaración.

    Al que más te guste de los dos le puedo añadir algo de cosecha propia.

    1. No escribas «ñapas».

    Y ahora tu me dices si todo lo que he citado no te parece una «ñapa».

    Te has ido por el camino más fácil, pero no he visto por ningún sitio ni un argumento a que te parece lo que te he dicho.

    Yo no le he enviado nada al pobre que pico las CompiledQueries, porque no lo considero pobre. Yo le cortaba el dedo al que lo diseño o pensó. Tu en que lado estás en el de los pobres o en el que diseña?

    Respecto a las collecciones, quiero que nos digas tú y no nos pases ningún link a la wiki, que todos sabemos buscar, sino te parecen «ñapas» que todas las asociadas a la interface de usuario, o mejor debería decir «UI» estén definidas como «object».

    Mira de nuevo te lo voy a decir yo. La única que se salva es MVC el resto son eso y no me quiero repetir.

    Como tu dices que VAR nos ahorra código, al igual que VB Y C#, tambien los config,xaml y demás calañas también generan o ejecutan código y a mí me parecen

    «CODE SMELL»

    Ves como cada uno tiene una opinión diferente, me parece estupendo el uso de «VAR» y así lo he dicho, pero igual de mal que a ti te parece mi demostración, que te repito no es más que devolver unos comentarios de Eduard a mí me parece igual de «CODE SMELL» esta última entrada de tu blog

    http://elbruno.visibli.com/share/xaCGDo

    donde preguntas por Result[0] y si no hay elementos.

    Ves como estás en el lado de los que piensa:)

    Saludos y vares con cervezas!

  10. Joer… que yo no he dicho que estoy en contra de var, sino que estoy en contra de usar var de forma general o por norma.

    A ver si saco tiempo para poder hacer una entrada, pero aviso que no es una entrada de mejor o peor, sino de experiencias y peligros que podrían aparecer a la hora de escribir código.

    Pero me daréis tiempo para ello y ya de paso, así se enfría el ambiente y no hay hachas ni gallofas con la palma abierta. XDDD

    P.D.: y digo yo… ¿para que usar otra cosa que no sea var?. ¿Al final va a tener razón Eduard y Microsoft debería obligar el uso de var?. Creo que ni tanto ni tan poco…

  11. @Pedro, pues lo dicho: ambos estamos a favor de var. Yo no le buscaré vueltas al respecto y seguiré feliz como una perdiz 😉

    Saludos

    PD: por cierto, gracias por el dato del código de mi blog, por suerte ese código no fue a producción todavía 😉

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *