Porqué es bueno eliminar la refencia al ensamblado Microsoft.VisualBasic.dll en nuestros proyectos (II)
Introducción
En mi última entrada, hacía referencia al porqué puede resultar positivo eliminar la referencia al ensamblado Microsoft.VisualBasic.dll en nuestros proyectos de Visual Basic. Lo cierto es que estas cosas no las encuentras en los libros ni te las cuentan prácticamente nadie, porque simplemente, muchas veces no nos cuestionamos algunas cosas. De vez en cuando es bueno preguntarse el porqué y cómo funcionan las cosas, más que dar por hecho una verdad absoluta.
De acuerdo al título de la entrada, en realidad eliminar tal y como pongo en la entrada es quizás una palabra demasiado drástica, pero es lo suficientemente drástica como para llamar la atención, que es lo que precisamente busco, pero quizás lo más razonable sería decir por ejemplo, tratar de evitar el uso de Microsoft.VisualBasic.dll siempre que podamos en lugar de eliminar, pero aún y así, lo más interesante es describir lo nocivo que puede ser resultar el uso de Microsoft.VisualBasic.dll en nuestros proyectos. Cuando llegue a las conclusiones de esta entrada, pregúntese si realmente está más de acuerdo con la palabra eliminar o con la frase evitar el uso, y sobre todo, pregúntese porqué.
No obstante, para sostener la afirmación de mi primera entrada realicé unos primeros y pequeños estudios que ahora trato de completar con un giro de tuerca más sobre el uso de buenas prácticas, las clases, las funciones y los métodos que .NET nos ofrece y que muchas veces obviamos en nuestros desarrollos Software.
Recordando los resultados del estudio anterior…
Para esta entrada, me baso en el estudio diferencial existente entre el ensamblado que usa Microsoft.VisualBasic.dll y el ensamblado al que hemos eliminado el uso de esta librería que vimos en la entrada anterior.
Adicionalmente, me apoyo en otro estudio y buenas prácticas que realicé en una entrada de Diciembre de 2006 y que podeis encontrar aquí esta entrada. En esa entrada, argumentaba un estudio sobre la iteración de cadenas en nuestras aplicaciones .NET. Argumentos y estudios que me sirven ahora para argumentar más aún algunas tesis que según mi modesto punto de vista, no son nada despreciables y que muestran las bondades de .NET vs el uso de instrucciones de Visual Basic 6 utilizadas en .NET.
Para tratar de ser justos, he vuelto a repetir las pruebas con JetBrains dotTrace que hice en la entrada anterior y he obtenido los siguientes datos:
La hebra de la aplicación (DatosCon) que utiliza el ensamblado que posee referencias a Microsoft.VisualBasic.dll tarda en total 21.722 ms, y en el proceso del control Button, 20.037 ms.
La hebra de la aplicación (DatosSin) que utiliza el ensamblado que no posee referencias a Microsoft.VisualBasic.dll tarda en total 17.692 ms, y en el proceso del control Button, 15.561 ms.
Es decir, la diferencia entre la hebra del primer (DatosCon) y segundo proceso (DatosSin) es de 4.030 ms. 4 segundos son muchos segundos.
Estudiando un poco más a fondo todos estos resultados, lo verdaderamente importante no es la comparación de la ejecución de la hebra completa, ya que el proceso entero está basado en hacer el clic manualmente sobre el control Button, y es posible que esa diferencia de milisegundos esté motivada por mi reacción y pulso más que otra cosa.
Así que donde me quiero parar realmente es en el estudio de la hebra del control Button, que es la que realmente me intesa.
Ahí, la diferencia entre ambos procesos es de 4.476 ms a favor de (DatosSin), es decir, el proceso de ejecución que utiliza el ensamblado que no tiene referencia a Microsoft.VisualBasic.dll es casi 4 segundos y medio más rápido que el mismo proceso de (DatosCon). Si miramos las pruebas que hice de la entrada anterior, la diferencia entre ambos procesos es de 4.465 ms, así que ms arriba ms abajo, estamos más o menos dentro de esos márgenes.
Girando la tuerca un poco más…
Pero claro… aquí no acaba esto, ya que lo más sensato es comparar los dos ensamblados.
Me parece estupendo que uno de ellos «gane«, pero ¿estamos seguros de que el ensamblado «ganador» es un ensamblado «perfecto«?.
Yo siempre digo que no hay nada perfecto, y cuando se habla de tecnología muchísimo menos. De hecho, lo voy a demostrar.
Utilizando nuevamente la herramienta JetBrains dotTrace, soy capaz de comparar ambos estudios y ver donde están los «cuellos de botella» o esas diferencias más notables.
En la siguiente imagen, podemos ver esas diferencias de forma gráfica:
Si atendemos a esta información, podremos observar que hay diferentes partes que están en color rojo.
Examinando todas las partes, nos damos cuenta de que hay una parte de la información que nos resulta llamativa. Me refiero a la parte del método TestConversion.
Según ese método, en el caso del uso de la librería WithoutReference.dll, el método que se ejecuta dentro del clic del control Button, consume la friolera cantidad de 15.539 ms de los 15.561 ms que consume todo el código contenido en el control Button.
Para estar utilizando funciones de .NET, el resultado no es que digamos esperanzador, por lo que lo primero que debemos hacer es estudiar el código de ese método.
Estudiando el código y aportando soluciones…
El método TestConversion tiene la funcionalidad de concatenar cadenas.
Concatenar cadenas es mucho más costoso que utilizar por ejemplo la clase StringBuilder que encontraremos en el nombre de espacio System.Text.
De acuerdo a la entrada que escribí en Diciembre del 2006 en el que mostraba la forma eficiente de trabajar con cadenas, deberíamos modificar el código de nuestra aplicación, para mejorar aún más el rendimiento del ensamblado que no utiliza Microsoft.VisualBasic.dll. El objetivo: crear un ensamblado lo más eficiente posible.
De esta forma, el código de nuestro ejemplo de acuerdo al método TestConversion, quedaría de la siguiente forma:
WithoutReference.Class1 (Class1.vb):
… Public Sub TestConversion() Dim auxiliarText As New System.Text.StringBuilder For i As Integer = 0 To 10000 If i Mod 2 = 0 Then ‘ Convert to String auxiliarText.Append(Convert.ToInt32(i).ToString) Else ‘ Convert to Integer (Int32) auxiliarText.Append(Convert.ToString(i)) End If Next End Sub … |
Si prestamos atención al código anterior, veremos que lo que hacemos es apoyarnos en la clase StringBuilder para realizar el mismo proceso.
En este punto, podría preguntarse si podríamos utilizar StringBuilder en el ensamblado que hace referencia a Microsoft.VisualBasic.dll. La respuesta es sí, por supuesto, pero si va a utilizar este objeto propio de .NET, ¿porqué no utiliza desde el principio solo y únicamente los objetos propios de .NET?. Se ahorraría problemas seguramente.
De hecho, el «cuello de botella» del ensamblado que hace uso de Microsoft.VisualBasic.dll está localizado dentro del mismo método, por lo que si utilizáramos StringBuilder, es prácticamente seguro que obtendríamos unos resultados muy óptimos, pero le aseguro, que los resultados más óptimos se encuentran localizados en el ensamblado que no hace uso de Microsoft.VisualBasic.dll. De todos los modos, aquí lo que tratamos de comparar son las funciones y métodos propios de Microsoft.VisualBasic.dll vs las funciones y métodos propios de .NET.
Si una vez modificado el código anterior ejecutamos nuestra aplicación, obtendremos unos resultados sorprendentes.
Estudiando los resultados…
De acuerdo a los nuevos resultados obtenidos, estos arrojan unos datos demoledores a favor de las instrucciones de .NET.
En la siguiente imagen podemos ver la tabla de resultados:
Ahora y gracias a StringBuilder, podemos indicar que la ejecución total de la hebra es de 1.932 ms, mientras que la ejecución del control Button que contiene todas las instrucciones que nos importan, es de 795,4 ms. Ni siquiera 1 segundo. Dentro de esta parte, seguimos observando que TestConversion sigue consumiendo gran parte del tiempo de proceso de ejecución, pero hemos pasado de 15.539 ms a 772,6 ms.
La diferencia es notable evidentemente.
Sobre ILDASM…
Utilizando la herramienta ILDASM que viene con .NET Framework y que permite estudiar el código intermedio de nuestras aplicaciones, podemos apreciar que las diferencias reales entre los dos ensamblados (el ensamblado que tiene la referencia a Microsoft.VisualBasic.dll y el ensamblado que no tiene referencia a ese ensamblado) son mínimas.
Si se estudia con detenimiento el código intermedio, se pueden ver algunos pequeños detalles en el ensamblado con referencia a Microsoft.VisualBasic.dll que no tiene el otro ensamblado, como algún ajuste de conversión.
De todos los modos, lo mejor es utilizar .NET Reflector y mirar las clases, métodos y funciones de Microsoft.VisualBasic.dll, y comprobar como funciona realmente «por debajo«.
Algunas conclusiones…
Ante esto tenemos una opción muy clara y evidente con una afirmación mucho más drástica si cabe: el uso exclusivo de Microsoft.VisualBasic.dll no nos favorece. Es decir, si lo que queremos es utilizar únicamente instrucciones de Microsoft.VisualBasic.dll, es prácticamente seguro que penalizaremos en rendimiento a nuestra aplicación. ¿Cuál es la penalización?. En realidad depende de cada proyecto, del código escrito en él, sus iteraciones, etc.
El uso híbrido de Microsoft.VisualBasic.dll e instrucciones puras .NET puede resultarnos útil y ventajoso. No es quizás lo más ventajoso, pero es claramente más favorable que el uso único y exclusivo de instrucciones heredadas de Visual Basic 6.
Si decidimos por un híbrido entre instrucciones puras .NET e instrucciones extraídas el ensamblado Microsoft.VisualBasic.dll, ¿porqué no utilizar directamente y únicamente las instrucciones de .NET?. Ese sería el escenario más adecuado y positivo. Por esa razón, una forma de habituarnos a trabajar sin el ensamblado Microsoft.VisualBasic.dll en Visual Studio 2008, es acceder a las Propiedades del proyecto y abrir la solapa de Referencias. Dentro de esa solapa, quitar la selección o la referencia al ensamblado Microsoft.VisualBasic.dll. De esta manera, el entorno nos avisará con un mensaje de error si estamos utilizando alguna instrucción de Visual Basic 6 habilitada por medio de Microsoft.VisualBasic.dll.
Así, ¿cuantas veces utiliza (yo lo hago) la instrucción VbCrLf?. Pues quizás no lo sepa, pero VbCrLf pertenece a la clase Contants del nombre de espacio Microsoft.VisualBasic. ¿Sorprendido?. Quizás lo lógico es utilizar rn en C#, o Environment.NewLine() en .NET, ¿pero VbCrLf?.
Como podemos ver, los «vicios» adquiridos y arrastrados por los desarrolladores que venimos de Visual Basic 6.0 son muchos, y erradicarlos va a ser un proceso muy largo y tedioso. ¿Porqué no empezar desde ya?.
13 Responsesso far
Hola Jorge, y una cuestión, a veces no somos conscientes del uso de esos elementos de esa librería, ¿cómo podemos saber cuándo estamos haciendo uso realmente y cómo sustituir por código .NET puro y duro?
Gracias.
Saludos.
Francisco J.
Hola Francisco,
básicamente podemos quitar como decía al final de la entrada, la referencia a Microsoft.VisualBasic.dll en Visual Studio 2008.
De esa forma, cuando utilicemos una clase, método o función, o incluso constante de ese ensamblado, nos dará un error.
Eso no quita, que cuando compilemos nuestra librería o aplicación, el ensamblado resultante agregue la referencia a Microsoft.VisualBasic.dll que será cargada en memoria.
Para quitar esa referencia como comentaba en la primera entrada de las tres que he hecho con esta, tenemos que ir a la línea de comandos y crear el ensamblado resultante quitando esa referencia.
Un rollo vamos… pero espero que más menos quede claro o que en Visual Studio 2010 simplifiquen y/o faciliten esta forma de trabajar «mejor». 🙂
Yo estoy totalmente de acuerdo contigo y de hecho, lo primero que hago es eliminar la referencia a VB, pero … Te has detenido a mirar el código del diseñador de un formulario aún habiendo eliminado la referencia a Microsoft.VisualBasic.dll? Te sorprenderás …
Peni, supongo que hablas de Application.Designer.vb y no de Form1.Designer.vb por ejemplo. ¿Porqué lo dices exactamente?, ¿por el supuesto uso de My y el uso de Microsoft.VisualBasic.ApplicationServices que se hace ahí?, ¿o por otra cosa?.
Esto es una linea del diseñador de un formulario:
Me.StockMiTextBox.CaracterPrompt = Global.Microsoft.VisualBasic.ChrW(95)
Y esto del diseñador de un DataSet:
«digoAlternativo, @PuntoVerde, @Libre1, @Libre2, @Libre3, @Libre4, @Libre5, @Loca»& _
«lizacion);»&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&»SELECT Id, Tienda, CodArt, Descripcion, Precio, IVA, Stock, StockMin»& _
Es un ejemplo, pero hay más, y te puedo garantizar que tendo la referencia ‘maldita’ quitada
Pues hombre, he dicho yo, con números me callas la bocota.
Viendo estas funciones, esta famosa librería sí tiene cosas complejas que afectan el rendimiento de una aplicación… Y segun lo que veo las cosas pueden ponerse muy feas. ¿Pero qué hay con las cosas útiles?
El namespace My.Application.FileSystem tiene un monton de cosas útiles, el namespace My.Settings tiene las configuraciones que están en el xml app.config (por usuario o por aplicación) como propiedades (codigo generado) lo cual me parece muy astuto y rápido.
¿Debemos dejar estas cosas útiles por las cosas dañinas que mensionas?
o acaso, ¿Estas cosas que mensiono (entre otras) también son dañinas?
Ahora, si tengo que admitir que todo TODO se puede hacer de otra forma sabiendo cuales son los objetos .NET que trabajan por detras, pero ahorrarse un par de líneas a costa de un par de overloads me parece un precio justo. Es simplemente una librería de utilitarios.
Hola delm,
coincido contigo en lo que comentas respecto a esas cosas útiles o mejor diría yo, simples y positivas de My (que reconozco que no son pocas).
Lo bueno de My es que reduce las líneas de código a escribir y simplifica mucho el código (algo que siempre he reconocido), aunque eso repercuta a veces en rendimiento y en mantenimiento (por poner un ejemplo, un programador de C# que va a mantener el código VB.NET no está acostumbrado a Microsoft.VisualBasic.dll ni debería estar acostumbrado a ello, ya que no es una librería .NET Framework completa, solo para VisualBasic).
Si prescindimos de My, entonces deberíamos escribir nuestros programas con los objetos de .NET como hacen otros lenguajes (C#, etc). Y se puede, pero a costa de escribir un poco más de código como muy bien apuntas, pero eso es así y mejor incluso, si conociéramos las librerías de .NET, algo que he comentado tantísimas veces desde que apareció la primera beta de .NET 1.0 (nuestra primera tarea es conocer .NET Framework por «dentro»).
¿Es justo, es dañino?. Como todo depende. La verdad absoluta nunca existe, y menos en informática. Lo bueno de este tipo de entradas y de vuestros comentarios, es que nos hagan reflexionar a todos un poco.
Midiéndolo en su justa medida, a lo mejor hay que ser permisivos con algunas cosas y menos permisivos con otras. Me explico para aclarar aún más esto (me da la sensación de que no termina de estar claro al 100%).
1) Por ejemplo, si vamos a escribir una librería, a lo mejor compensa escribirla evitando el uso de Microsoft.VisualBasic.dll. Con ello, evitaremos el uso de Mid, Left, etc y no arrastraremos la referencia a ese ensamblado, algo que nos beneficia por crear ensamblados más livianos, reutilizables y migrables más fácilmente.
2) Si vamos a escribir una aplicación Windows, yo quitaría la referencia a Microsoft.VisualBasic.dll para evitar usar funciones y métodos generalistas dentro del formulario como Mid, Left, etc.
3) Por debajo, el diseñador como decía Peni, es posible que cree código con referencias a My etc. En muchos de los casos, lo que hace es extender My, algo que se puede hacer sin necesidad de arrastrar Microsoft.VisualBasic.dll, ya que bastaría con crear una clase dentro de un namespace My. Pero en mi opinión, es bueno eliminar la referencia a este ensamblado «maldito». Luego, en tiempo de compilación, es posible que Visual Studio nos agregue una referencia a Microsoft.VisualBasic.dll porque es posible que My se utilice en otros sitios, pero nuestras librerías no tendrán esa refencia y serán más livianas y directas. Además, forzaremos a que la aplicación Windows no utilice «malas prácticas». No sé si ahora se me entiende mejor.
Son tres posibilidades. Lo fácil es ponerse la manta en la cabeza y «pasar de todo». Pero a lo mejor en un determinado proyecto nos encontramos con problemas de rendimiento y escalabilidad, o de cualquier otra índole… y no sabemos que cosas podrían estar perjudicándonos.
Resumiendo, lo que quiero decir es que dependiendo del tipo de aplicación que escribamos, a lo mejor al final es necesario (porque no hay remedio) que el compilador agregue una referencia a Microsoft.VisualBasic.dll, pero en nuestras librerías y en nuestro código por ejemplo, podríamos evitarlo. Si Visual Studio lo agrega, que lo agregue, pero nosotros podemos evitar su uso en un porcentaje muy elevado, y con ello, nos habituaremos a escribir código .NET puro.
La primera norma es quitar la referencia a Microsoft.VisualBasic.dll, y luego ser conscientes del rendimiento y tratar de hacer librerías o ensamblados lo más livianos (como decía) posibles dentro de nuestro ámbito.
No obstante, esperemos que Visual Studio 2010 resuelva en parte estos problemas o que dé al programador la opción de incluir esta librería o no. Porque lo cierto, y ahí no digo nada porque sería harina de otro costal, quitar la referencia de Microsoft.VisualBasic.dll no es aún una tarea trivial, aunque sí debería serlo en mi opinión.
Aprovecho para daros las gracias a todos por dar vuestras opinones.
Un saludo.
Jorge
y no es mejor, alomejor, hacerse uno mismo una clase que haga las cosas mas faciles en lugar de para usar el namespace My importar el tocho de libreria?
La podemos llamar HelpMe o algo asi ;P
😉
Interesante la prueba, pero está claro que sentencias com ChrW y AscW no van a mejorar usando sus alternativas System.Convert.
El Compilador de VB, generará código optimizado para ChrW(10) y AscW(«A») per no para las equivalentes.
Tampoco creo razonable renunciar a InputBox y Beep ni a la lista de constantes definidas en la librería a las que ya estamos acostumbrados.
Lo curioso surge cuando inspeccionas un poco la librería en cuestión con el software Reflector y te llevas sorpresas, como con la función Hex(Object), en el que todavía sigues viendo el uso del GOTO, aunque es una anécdota, el empleo de .net puro para solucionar la «perdida de rendimiento de estas funciones» no es que le hayan dedicado mucho tiempo.
Más bien viene a refutar la dirección que están tomando estos artículos sobre que es una medida temporal que se utilizo para que los programadores de VB6 tuvieran una transición «fácil» a .Net, pero creo que en VS2008 o ya para el VS2010 deberían eliminar esta referencia «obligatoria» para el compilar y que fuera opcional, ya que al final lo único que esta promoviendo son los malos habitós.
Tengo un problema referido a este tema.
Ya he ido sustituyendo las referencias a la libreria como explicas (el articulo me resulto convincente).
Pero en el caso de utilizar en un String el IndexOf(» «c) para buscar el primer espacio resulta q si tengo mas de unos consecutivo entonces me da la posicion del ultimo.
Explico:
«ABC DEF»
Pos = MyString.IndexOf(» «c)
Da como resultado Pos = 5.
Cuando el resultado buscado es 3.
Algo q me falta como parametro ?
Saludos,
@
mmm muy bueno el post.. y yo encantado con eliminar visualbasic.dll porque necesito rendimiento a lo bruto pero… como me deshago de vbCrLf (creando una constante) o de VbTab… porque en c# perfecto pero en vb no lo veo tan claro para crear una constante…
Saludos!
Vale, soy yo de nuevo… dandole al coco supongo que podría crear una dll en c#, definir las constantes que me hicieran falta del visualbasic.dll y así poder eliminar esta referencia de forma más sencilla…