var en c# si, o var no

Published 16/5/2012 17:55 | Pedro Hurtado

Este es de esos post donde uno piensa exactamente igual que cuando deshoja una margarita. Me quiere no me quiere. Mi opinión antes de seguir es que la quiero:).

Todo esto surge a raíz de una conversación en twitter que podéis seguir aquí y que lógicamente expuso diferentes opiniones que al final es en lo que consiste, Yo opino, tu opinas.

¿Quien tiene la razón? todos:).

Un poco de puesta en escena

https://twitter.com/#!/_PedroHurtado/status/202057547094564864, claro yo haciendo caso omiso a los consejos de mi padre “Pedro Dios te dio dos orejas para escuchar el doble de lo que hablas”. No se me ocurre otra cosa que lanzar esto:).

https://twitter.com/#!/_PedroHurtado/status/202058984570621953 y después no conforme con esto un poco más de fuerza bruta.

https://twitter.com/#!/_PedroHurtado/status/202060219935756288

Con lo cual surge todo esto.

https://twitter.com/#!/_PedroHurtado/status/202061813746110464 y más. Que no es necesario hacerse pesado y vamos al grano.

Que es var?

De las pocas cosas que da gusto leer en el MSND y uno se entera. Es la definición de Variables locales con asignación implícita de tipos (Guía de programación de C#).

Pero si nos centramos en esto vemos cosas que deberían tener algún que otro comentario.

“A las variables locales se les puede asignar un "tipo" deducido var en lugar de un tipo explícito. La palabra clave var indica al compilador que deduzca el tipo de la variable a partir de la expresión que se encuentra en el lado derecho de la instrucción de inicialización”

Efectivamente Eduard(@eiximenis), discrepo totalmente en tu afirmación “cuando el tipo del lvalue es distinto al tipo del rvalue... ;)” no creo que lvalue entendido como la parte izquierda sea distinto jamás al rvalue es decir el lvalue es deducido o mejor dicho inferido a partir del rvalue, con lo cual todo es valido para “var” y lo vamos a ver:).

@Jonathan_JFR, te voy a explicar porque los tipos anónimos o lambda expresion no se pueden inferir, para ello te recomiendo esta lectura Why can't an anonymous method be assigned to var?. En la cual el gran Eric Lippert metió la pata hasta donde se puede meter:), me quedo con la respuesta de Mehrdad.

“@Eric: Good answer, but it's slightly misleading to illustrate this as something that's not possible, as this actually works completely fine in D. It's just that you guys didn't choose to give delegate literals their own type, and instead made them depend on their contexts... so IMHO the answer should be "because that's how we made it" more than anything else. :)”

Pues si Eriq es confuso y te voy a poner un ejemplo sencillo.

var i = 123;

Esto que es?

Un int,uint,short,ushort,long,ulong,sbyte,byte

¿Por qué se infiere un int?

Creo que “porque así se decidió”.

Con lo cual tu para inferir un "tipo integrado” o un ValueType te tienes que ir a la siguiente tabla.

Tabla de valores predeterminados (Referencia de C#).

En resumen si yo quiero hacer que i sea del tipo Decimal tengo que hacer esto.

var i=123M; o bien var i = 123m; es decir hacer uso de los Literales y más concretamente de los sufijos de estos o bien en el caso de short que no lo tiene hacer uso de lo siguiente.

var i=(short)0;

A partir de este momento es fácil deducir como una Lambda Expresion o bien un Método Anónimo lo puedo poner a la derecha de “var”.

   1: var i = delegate(int y) //Error No se puede asignar metodo anonimo a una variable local tipificada implicitamente
   2: {
   3:    return y * 2;
   4: };
   5: var i = ((Func<int,int>)delegate(int y) //Ok
   6: {
   7:    return y * 2;
   8: });

Ahora vamos con las Lambda Expresion, estás pueden devolver o un Func,etc,etc o bien o una Expresión es decir esto sería un Func.

   1: Func<int, int, int> b = (x,y)=>x*y;

o eso mismo podría ser

   1: Expression<Func<int, int, int>> b = (x, y) => x * y;

Alguien me impide a mi hacer esto.

   1: var i = (int x, int y) => x * y;

Si el compilador, pero que ocurre si hago lo siguiente, pues que todo funciona.

   1: var i = (Func<int,int,int>)((x,y)=>x+y);
   2: var resultado = i(10, 10);
   3: Console.WriteLine(i.GetType());
   4: Console.WriteLine(resultado.GetType());
   5: Console.WriteLine(resultado);

Os imagináis el resultado, pues os lo muestro.

System.Func`3[System.Int32,System.Int32,System.Int32]
System.Int32
20

Otra cosa, puede hacer esto?.

var i = null, pues no y para ello lo mejor que podemos es revisar lo siguiente. Implicitly Typed Local Variables, concretamente el apartado de “restrictions”.

Pues sí puedo hacer y muchas cosas más.

   1: var nullint32 =(int?)0;
   2: var nulo = (Clase)null;
   3: var xByte = (byte)0;
   4: var Xdecimal= 0M;
   5: var XLong = 0L;
   6: var XDouble = 0D;
   7: var XUint = 0U;
   8: var XShort = 0;
   9: var xFloat = 0F;
  10: var XChar = '1';
  11: var XEnum = (aaa)1;
  12: var xShort = (short)32767;

Y de esta forma todas las que se os ocurran:).

Conclusiones.

1. Que como todo tiene su razón de ser, que se puede utilizar y ahorrar trabajo es evidente.

2. Que si  abusamos puede llegar a ser malo, también.

3. Yo lo utilizo siempre en linq, tipos anonimos,al crear un nuevo objeto, en using,devoluciones de metodos y foreach y hasta el momento no he tenido ningún problema.

4. Es el Dim de Vb. Nooooooooooooooo.

5. Es un variant de vb o bien un object de c#. Nooooooooo, si revisamos el IL vemos que exactamente tanto una como la otra sentencia genera lo mismo.

int i = 10;

var i=10;

6. Es confuso Noooooooooo, para mi confuso es no poner nombres claros a las variables, eso si que es confuso.

Archivado en: ,
Comparte este post:

Comentarios

# Juan Carlos Ruiz Pacheco said on May 16, 2012 7:15 PM:

Muy bien , salvo que para definir un tipo decimal no es 123D sino 123m .

# Pedro Hurtado said on May 16, 2012 7:25 PM:

Juan Carlos, solucionado:)

123M o 123m.

# Eduard Tomàs i Avellana said on May 16, 2012 8:05 PM:

A ver... ;-)

Bueno... releyendo tweets, está claro que me expresé mal :(

Lo que quería decir (y veo que no se entiende) es "cuando se quiera que el tipo del lvalue sea distinto al del rvalue". Recuerda que estaba contestando a "cuando NO se puede usar var".

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 ;-)

Del resto del post, nada más que decir... Estamos de acuerdo en todo, pero SOBRETODO en el punto 6 final (en todo caso igual discrepo del 2 ya que para mi no existe el "abuso" de var).

;-)

Un abrazo fiera!

# Jorge Serrano said on May 16, 2012 11:34 PM:

Pedro, felicidades por tu entrada.

Me ha encantado y algunas de las cosas que comentas las tengo pensadas comentar (a mi manera, pero comentarlas al fin y al cabo).

Yo por regla general, prefiero NO usar var.

Repito,... por regla general. No he dicho NO usar var NUNCA. :)

Tal y como dije por Twitter, para mí el uso indiscriminado de var implica dejadez y ser vagos, porque sino al final, terminaremos usando SIEMPRE var, por eso digo que por regla general prefiero NO usar var.

Y no continúo que sino no tendré a lo mejor nada que decir en mi entrada.

Pedro, me has ganado... tú ya has escrito tu entrada sobre var, yo la mía la tengo más verde y no sé cuando la publicaré. A ver si saco ratos y puedo hacerlo pronto.

Lo dicho, buena entrada fenómeno. :)

# Eduard Tomàs i Avellana said on May 17, 2012 8:44 AM:

xD

Jejejee... Espero tu entrada Jorge y ojo que como entre Hadi con toda la caballería verás! ;-) :P

Pedro, lo que comentas sobre la no posible asignación de una lambda a var me lo encontré yo hace algún tiempo, y como es de menester también me quejé por twitter:

http://twitter.com/#!/eiximenis/status/179237257948041216

De hecho yo voy un paso más allá que tu porque creo e invoco la lambda.

Tanto tu caso como el mío *podrían* compilar.

En tu caso porque pones los tipos de los parámetros en el lambda y por lo tanto están claros cuales son esos tipos :P

En el mio, porque yo hago:

var a = (x => x + 10)(20);

Estoy invocando la lambda pasándole un 20, ergo x vale 20, ergo x es int, ergo la lambda es asignable a Func<int, int>.

La razón por la que eso no funciona es simple y llanamente que no está implementado. Y seguramente no lo está porque aquí es donde flaquea el tema de los delegados. El tema de los delegados funciona muy bien, hasta el momento en que empezamos a migrar a una programación mucho más funcional.

Y es que, no nos engañemos Pedro, las funciones NO son "ciudadanos de primera clase" en C#. Los delegados nos dan esa impresión. Pero NO lo son.

Un ejemplo rápido de lo que quiero decir... SUPÓN, repito SUPÓN (porque NO lo hace) que ESO compila, y que obvialemente r es un int (con valor 30):

var r = (x=>x+10)(20);

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...

Saludos!

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

# Lluis Franco said on May 17, 2012 10:05 AM:

Debo deciros que yo hasta hace menos de 2 años era del partido "no-uso-var-siempre-solo-con-tipos-dinamicos", pero un par de posts de Edu y Hadi me hicieron pensar sobre el tema, y me di cuenta de que realmente lo hacía por costumbre.

Desde entonces he abrazado var y soy un hombre nuevo. Ahora sólo defino el tipo en aquellas ocasiones en las que me parece realmente necesario (algunas lambdas capciosas y poco más).

En cuanto al código que propone Edu:

var a = (x => x + 10)(20);

Lógicamente no compila (bueno, no está implementado). Pero si definimos los tipos correctamente para que el compilador trague:

var a1 = new Func<int, int>(x => x + 10)(20);

Resulta que el optimizador de código de CodeRush me dice que la declaración... es redundante! Y me propone en su lugar:

var a2 = x => x + 10(20);

Que ya hemos visto que no compila :-S

Curioso... esto pasa con R#?

Saludos,

# El Blog de Pedro Hurtado said on May 17, 2012 12:42 PM:

Este post viene como consecuencia de los comentarios del gran @eiximenis , en este otro var en c# si

# Pedro Hurtado said on May 17, 2012 12:46 PM:

@Jorge

Muchas gracias por los comentarios, espero ansioso tu reflexion:).

@Lluis

Muchas gracias, pero la respuesta hay que pedírsela a CodeRush:)

@Eduard la fiera se ha comido al gatito, contestado:)

geeks.ms/.../var-ii-los-hermanos-de-simba.aspx