Uso del tipo “var” en statement de variable

Nuestra auditoría interna nos sugiere usar una statement de tipo de variable explícita en lugar de usar la palabra clave var . Ellos argumentan que el uso de var “puede llevar a resultados inesperados en algunos casos”.

No tengo conocimiento de ninguna diferencia entre la statement explícita de tipo y el uso de var una vez que el código se comstack en MSIL.

El auditor es un profesional respetado, así que no puedo simplemente rechazar tal sugerencia.

Qué tal esto…

 double GetTheNumber() { // get the important number from somewhere } 

Y luego en otro lugar …

 var theNumber = GetTheNumber(); DoSomethingImportant(theNumber / 5); 

Y luego, en algún momento en el futuro, alguien se da cuenta de que GetTheNumber solo devuelve números enteros, por lo que los refactores devuelven int lugar de double .

¡Explosión! No hay errores de comstackción y empiezas a ver resultados inesperados, porque lo que antes era aritmética de punto flotante ahora se ha convertido en aritmética de enteros sin que nadie se dé cuenta.

Habiendo dicho eso, este tipo de cosas deberían ser atrapadas por tus pruebas de unidad, etc., pero todavía es un potencial.

Tiendo a seguir este esquema:

 var myObject = new MyObject(); // OK as the type is clear var myObject = otherObject.SomeMethod(); // Bad as the return type is not clear 

Si el tipo de retorno de SomeMethod cambia, este código aún se comstackrá. En el mejor de los casos, obtendrá errores de comstackción más adelante, pero en el peor de los casos (dependiendo de cómo se utilice myObject ), es posible que no lo haga. Lo que probablemente obtendrá en ese caso son los errores en tiempo de ejecución que podrían ser muy difíciles de localizar.

Algunos casos realmente podrían llevar a resultados inesperados. Soy un fanático de var , pero esto podría salir mal:

 var myDouble = 2; var myHalf = 1 / myDouble; 

Obviamente, esto es un error y no un “resultado inesperado”. Pero es un gotcha …

var no es un tipo dynamic, es simplemente azúcar sintáctico . La única excepción a esto es con los tipos anónimos. De los documentos de Microsoft

En muchos casos, el uso de var es opcional y es solo una conveniencia sintáctica. Sin embargo, cuando una variable se inicializa con un tipo anónimo, debe declarar la variable como var si necesita acceder a las propiedades del objeto en un momento posterior.

No hay diferencia una vez comstackdo en IL a menos que haya definido explícitamente el tipo como diferente al que estaría implícito (aunque no puedo pensar por qué lo haría) . El comstackdor no le permitirá cambiar el tipo de una variable declarada con var en ningún punto.

De la documentación de Microsoft (de nuevo)

Una variable local de tipo implícito se escribe fuertemente como si usted mismo hubiera declarado el tipo, pero el comstackdor determina el tipo

En algunos casos, var puede mejorar la legibilidad. Más estados de Microsoft docs :

El uso de var tiene al menos el potencial de hacer que su código sea más difícil de entender para otros desarrolladores. Por esa razón, la documentación de C # generalmente utiliza var solo cuando es necesario.

En el mundo no genérico, puede obtener un comportamiento diferente al usar var lugar del tipo siempre que se produzca una conversión implícita, por ejemplo, dentro de un bucle foreach .

En el siguiente ejemplo, se lleva a cabo una conversión implícita del object a XmlNode (la interfaz no genérica IEnumerator solo devuelve el object ). Si simplemente reemplaza la statement explícita de la variable de bucle con la palabra clave var , esta conversión implícita ya no tendrá lugar:

 using System; using System.Xml; class Program { static void Foo(object o) { Console.WriteLine("object overload"); } static void Foo(XmlNode node) { Console.WriteLine("XmlNode overload"); } static void Main(string[] args) { XmlDocument doc = new XmlDocument(); doc.LoadXml(""); foreach (XmlNode node in doc.DocumentElement.ChildNodes) { Foo(node); } foreach (var node in doc.DocumentElement.ChildNodes) { // oops! node is now of type object! Foo(node); } } } 

El resultado es que este código realmente produce diferentes salidas dependiendo de si usó var o un tipo explícito. Con var se ejecutará la sobrecarga de Foo(object) , de lo contrario, se ejecutará la sobrecarga de Foo(XmlNode) . La salida del progtwig anterior por lo tanto es:

 Sobrecarga de XmlNode
 sobrecarga de objetos

Tenga en cuenta que este comportamiento es perfectamente de acuerdo con la especificación del lenguaje C #. El único problema es que var infiere un tipo diferente ( object ) de lo que cabría esperar y que esta inferencia no es obvia al mirar el código.

No agregué el IL para que sea breve. Pero si lo desea, puede echar un vistazo con ildasm para ver que el comstackdor en realidad genera instrucciones IL diferentes para los dos bucles foreach.

Es un reclamo extraño que el uso de var nunca se debe usar porque “puede dar lugar a resultados inesperados en algunos casos”, porque hay sutilezas en el lenguaje C # mucho más complejas que el uso de var .

Uno de estos son los detalles de la implementación de los métodos anónimos que pueden llevar a la advertencia de R # “Acceso al cierre modificado” y un comportamiento que no es lo que se podría esperar de ver el código. A diferencia de var que puede explicarse en un par de oraciones, este comportamiento toma tres publicaciones de blog largas que incluyen la salida de un desensamblador para explicar completamente:

  • La implementación de métodos anónimos en C # y sus consecuencias (parte 1)
  • La implementación de métodos anónimos en C # y sus consecuencias (parte 2)
  • La implementación de métodos anónimos en C # y sus consecuencias (parte 3)

¿Esto significa que tampoco debe usar métodos anónimos (es decir, delegates, lambdas) y las bibliotecas que dependen de ellos como Linq o ParallelFX solo porque en determinadas circunstancias extrañas el comportamiento podría no ser el esperado?

Por supuesto no.

Significa que debe comprender el lenguaje en el que está escribiendo, conocer sus limitaciones y los casos límite, y probar que las cosas funcionan como espera. La exclusión de las funciones de idioma sobre la base de que “pueden dar lugar a resultados inesperados en algunos casos” significaría que le quedaron muy pocas funciones de idioma para usar.

Si realmente quieren discutir el lanzamiento, pídales que demuestren que algunos de sus errores pueden atribuirse directamente al uso de var y que la statement de tipo explícita los habría evitado. Dudo que tengamos noticias de ellos pronto.

Argumentan que el uso de var “puede llevar a resultados inesperados en algunos casos” .a resultados inesperados en algunos casos “.

Si lo inesperado es “No sé cómo leer el código y averiguar qué está haciendo”, entonces sí, puede dar lugar a resultados inesperados. El comstackdor tiene que saber qué tipo para hacer la variable en función del código escrito alrededor de la variable.

La palabra clave var es una característica de tiempo de comstackción. El comstackdor pondrá el tipo apropiado para la statement. Por eso no puedes hacer cosas como:

 var my_variable = null or var my_variable; 

La palabra clave var es excelente porque, tienes que definir menos información en el propio código. El comstackdor se da cuenta de lo que se supone que debe hacer por ti. Es casi como progtwigr siempre en una interfaz cuando la usa (donde los métodos y las propiedades de la interfaz se definen por lo que usa dentro del espacio de statement de la variable definida por var). Si el tipo de una variable necesita cambiar (dentro de lo razonable, por supuesto), no necesita preocuparse por cambiar la statement de la variable, el comstackdor se encarga de esto por usted. Esto puede parecer un asunto trivial, pero qué sucede si tiene que cambiar el valor de retorno en una función, y esa función se usa en todo el progtwig. Si no usó var, entonces tiene que buscar y reemplazar cada lugar al que se llama esa variable. Con la palabra clave var, no tienes que preocuparte por eso.

Cuando se presentan pautas, como lo tiene que hacer un auditor, es probablemente mejor errar por el lado de tontos seguros , es decir, enumerar las buenas prácticas / enumerar las malas prácticas en lugar de decirles a las personas que simplemente sean sensatas y hagan lo correcto. cosa basada en una evaluación de la situación en cuestión .

Si solo dices “no uses var en ningún lugar del código”, eliminas mucha ambigüedad en las pautas de encoding. Esto debería hacer que el código se vea y se sienta más estandarizado sin tener que resolver la pregunta de cuándo hacer esto y cuándo hacerlo.

Yo personalmente amo var . Lo uso para todas las variables locales. Todo el tiempo. Si el tipo resultante no está claro, entonces este no es un problema con var , sino un problema con los (nombres de) métodos utilizados para inicializar una variable …

Sigo un principio simple cuando se trata de usar la palabra clave var. Si conoce el tipo de antemano, no use var. En la mayoría de los casos, uso var con linq, ya que es posible que desee devolver un tipo anónimo.

Var es mejor usar cuando tienes statement obviamente

 ArrayList en = new ArrayList() 

complica la legibilidad

 var en = new ArrayList() 

Perezoso, claro codigo, me gusta

Uso var solo donde está claro qué tipo de variable es la variable, o donde no es necesario saber el tipo en absoluto (por ejemplo, GetPerson () debería devolver Person , Person_Class , etc.).

No uso var para tipos primitivos, enumeración y cadena. Tampoco lo uso para el tipo de valor, porque el tipo de valor se copiará por asignación, por lo que el tipo de variable debe declararse explícitamente.

Acerca de los comentarios de su auditor, yo diría que agregar más líneas de código como lo hemos estado haciendo todos los días también “conduce a resultados inesperados en algunos casos” . Esta validez de argumento ya ha sido demostrada por los errores que creamos, por lo tanto, sugeriría congelar el código base para siempre para evitarlo.

usar var es un código perezoso si sabes cuál será el tipo. Es más fácil y limpio de leer. Cuando se busca mucho y mucho código, más fácil y limpio es siempre mejor.

No hay absolutamente ninguna diferencia en la salida de IL para una statement de variable que usa var y una especificada explícitamente (puede probar esto utilizando el reflector). Generalmente solo uso var para tipos generics nesteds largos, bucles foreach y tipos anónimos, ya que me gusta tener todo explícitamente especificado. Otros pueden tener diferentes preferencias.

var es solo una notación abreviada del uso de la statement de tipo explícita.

Solo puedes usar var en ciertas circunstancias; Tendrá que inicializar la variable en el momento de la statement cuando use var. No puede asignar una variable que sea de otro tipo después de la variable.

Me parece que muchas personas tienden a confundir la palabra clave ‘var’ con el tipo de datos ‘Variant’ en VB6.

El “único” beneficio que veo hacia el uso de la statement de variable explícita, es que con los nombres de tipos bien elegidos usted declara la intención de su código mucho más clara (lo cual es más importante que cualquier otra cosa). El beneficio de la palabra clave var es realmente lo que dijo Pieter.

También creo que se encontrará con problemas si declara sus dobles sin la D al final. cuando compile la versión de lanzamiento, es probable que su comstackdor elimine el doble y los convierta en una flotación para ahorrar espacio, ya que no considerará su precisión.

var comstackrá lo mismo que el tipo estático que se podría especificar. Simplemente elimina la necesidad de ser explícito con ese tipo en su código. No es un tipo dynamic y no puede / no puede cambiar en tiempo de ejecución. Me parece muy útil para usar en bucles foreach.

 foreach(var item in items) { item.name = ______; } 

Cuando se trabaja con enumeraciones, algunas veces se desconoce el tiempo que se necesita para realizar búsquedas en un tipo específico. El uso de var en lugar del tipo estático dará el mismo resultado. También he encontrado que el uso de var se presta a una refactorización más fácil. Cuando se utiliza una enumeración de un tipo diferente, no será necesario actualizar foreach.

El uso de var puede ocultar errores de progtwigción lógica, de lo contrario, habría recibido una advertencia del comstackdor o del IDE. Vea este ejemplo:

 float distX = innerDiagramRect.Size.Width / (numObjInWidth + 1); 

Aquí, todos los tipos en el cálculo son int , y usted recibe una advertencia sobre una posible pérdida de fracción porque recoge el resultado en una variable float .

Utilizando var:

 var distX = innerDiagramRect.Size.Width / (numObjInWidth + 1); 

Aquí no distX ninguna advertencia porque el tipo de distX se comstack como int . Si pretendía usar valores flotantes, este es un error lógico que está oculto para usted y difícil de detectar en la ejecución a menos que active una excepción de divide by zero en un cálculo posterior si el resultado de este cálculo inicial es <1.