Una lista exhaustiva de diferencias entre VB.NET y C #. Parte 1

imagen

Según la calificación de TIOBE en 2018, VB.NET superó a C # en popularidad. Coincidencia o no, pero en febrero Eric Lippert, uno de los creadores de C #, instó a los lectores a prestar atención al blog de su amigo, un ex compañero de equipo del compilador de Roslyn y, en combinación, un ardiente fanático de VB.NET, Anthony Green . "Tales recursos son detalles detallados de expertos que no son tan fáciles de encontrar leyendo la documentación", escribe Eric. Presentamos a su atención la primera parte de la traducción del artículo de Anthony Green "Una lista exhaustiva de diferencias entre VB.NET y C #". Quizás es precisamente en estas diferencias donde reside el secreto de la dinámica de la calificación de estos idiomas.

Durante casi la mitad de mi vida, he sido testigo y participé en innumerables debates sobre cuán similares o diferentes son los dos lenguajes .NET más populares. Primero, como aficionado, luego como profesional y, finalmente, como defensor del cliente, gerente de programa y diseñador de idiomas, puedo decir sin exagerar cuántas veces escuché o leí algo como:
"... VB.NET es realmente una capa delgada encima de IL, como C # ..."
o
"... VB.NET es en realidad solo C # sin punto y coma ..."
Como si los idiomas fueran una conversión XML o una hoja de estilo.

Y si algún visitante ardiente no escribe esto en un comentario, a menudo esto está implícito en preguntas como: “¡Hola, Anthony! Me encontré con una diferencia tan pequeña en un solo lugar: ¿es esto un error? ¿Cómo podrían estos dos idiomas idénticos, que deberían ser idénticos en nombre de todos los buenos y santos de este mundo, dispersarse en este único lugar? ¿Por qué necesitamos tal injusticia?

" Separados " , como si fueran lo mismo hasta que ocurrió una mutación, y luego se convirtieron en especies separadas. Ja!

Pero entiendo eso. Antes de unirme a Microsoft, también podría haber mantenido vagamente esta idea y utilizarla como argumento para responder a los oponentes o tranquilizar a alguien. Entiendo su encanto. Es fácil de entender y muy fácil de repetir. Pero trabajando en Roslyn (esencialmente reescribiendo VB y C # completamente desde cero) durante 5 años, me di cuenta de lo inequívocamente falsa que es esta idea . Trabajé con un equipo de desarrolladores y probadores para volver a implementar cada pulgada de ambos idiomas, así como sus herramientas en una gran solución multiproyecto con millones de líneas de código escritas en ambos idiomas. Y teniendo en cuenta la gran cantidad de desarrolladores que cambian entre ellos de un lado a otro, y el alto nivel de compatibilidad con los resultados y la experiencia de las versiones anteriores, así como la necesidad de reproducir de manera confiable el gigantesco volumen de API con gran detalle, me vi obligado a conocer las diferencias muy de cerca. De hecho, a veces me pareció que aprendía algo nuevo sobre VB.NET (mi idioma favorito) todos los días.

Y finalmente, me tomé el tiempo para sentarme y descargar del cerebro una partícula de lo que aprendí usando y creando VB.NET en los últimos 15 años, con la esperanza de que al menos pueda ahorrar mi tiempo la próxima vez.

Antes de pasar a la lista, describiré las reglas básicas:

  • Esta lista no es exhaustiva en el sentido habitual. El es exhaustivo. Estas no son todas las diferencias. Estas ni siquiera son todas las diferencias que generalmente conozco. Estas son solo las diferencias que puedo recordar primero, hasta que me canse demasiado para continuar; hasta que me quede sin fuerzas. Si yo o alguno de ustedes conoce o recuerda otras diferencias, me complacerá actualizar esta lista.
  • Comenzaré al comienzo de la especificación VB 11 y descenderé utilizando sus contenidos para recordarme las diferencias que se me ocurren primero sobre este tema.
  • Esta NO es una lista de funciones en VB que no están en C #. Por lo tanto, no hay "literales XML versus punteros". Esto es demasiado común, y ya hay toneladas de tales listas en Internet (algunas de las cuales fueron escritas por mí, y tal vez en el futuro escribiré más). Me enfocaré principalmente en construcciones que tienen un análogo en ambos idiomas, y donde un observador no informado puede sugerir que estas dos cosas se comportan igual, pero donde hay pequeñas o grandes diferencias; pueden tener el mismo aspecto, pero funcionan de manera diferente o, en última instancia, generan un código diferente.
  • Esta NO es una lista de diferencias sintácticas entre VB y C # (que son innumerables). Hablaré principalmente sobre las diferencias semánticas (qué significan las cosas), y no sobre las sintácticas (cómo se escriben las cosas). Por lo tanto, no hay piezas como "VB comienza a comentar con ', y C # usa //" o "en C # _ es un identificador válido, pero no en VB". Pero romperé esta regla por varios casos. Después de todo, la primera sección de la especificación trata sobre reglas léxicas.
  • Muy a menudo, daré ejemplos, y a veces ofreceré justificaciones por las cuales el diseño podría ir de una forma u otra. Algunas decisiones de diseño se tomaron ante mis ojos, pero la gran mayoría precedió a mi tiempo, y solo puedo adivinar por qué se tomaron.
  • Por favor, deje un comentario o twittee ( @ThatVBGuy ) para hacerme saber sus diferencias favoritas y / o las que le gustaría saber más.

Habiendo definido expectativas y sin más demoras ...

Contenido


Texto oculto

Sintaxis y preprocesamiento



Anuncios, etc.



Instrucciones



Sintaxis y preprocesamiento


1. Las palabras clave y los operadores de VB pueden usar caracteres de ancho completo


Algunos idiomas (no sé cuántos exactamente, pero al menos en algunas formas de chino, japonés y coreano) usan caracteres de ancho completo. En resumen, esto significa que cuando se usa una fuente monoespacial (como lo hacen la mayoría de los programadores), el carácter chino ocupará el doble de espacio horizontal que los caracteres latinos que solíamos ver en el oeste. Por ejemplo:



Aquí tengo una declaración de variables escrita en japonés, y la inicialización con una cadena también escrita en japonés. Según el traductor de Bing, la variable se llama "saludo" y la línea dice "¡Hola, mundo!" El nombre de la variable en japonés tiene solo 2 caracteres de longitud, pero ocupa el espacio de 4 caracteres de ancho medio que mi teclado generalmente muestra, como lo demuestra el primer comentario. Hay versiones de números de ancho completo y todos los demás caracteres ASCII impresos que tienen el mismo ancho que los japoneses. Para demostrar esto, escribí un segundo comentario usando los números de ancho completo "1" y "2". Estos no son "1" y "2" como en el primer comentario. No hay espacios entre los números. También puede ver que el tamaño de los caracteres no es exactamente de 2 caracteres de ancho, hay un ligero desplazamiento. Esto se debe en parte a que este programa mezcla caracteres de ancho completo y medio ancho en una línea y en las tres líneas.

Los espacios son de ancho medio, los caracteres alfanuméricos son de ancho completo. No somos programadores a menos que estemos obsesionados con la alineación del texto. Y me parece que si usted es chino, japonés o coreano (u otra persona que usa caracteres de tamaño completo para su idioma) y usa identificadores o cadenas escritos en su idioma nativo, estos errores menores de alineación son irritantes.

Según tengo entendido, dependiendo de su teclado japonés, cambiar entre jeroglíficos y latín es fácil, pero es preferible usar caracteres latinos de ancho completo. VB admite esto en palabras clave, espacios, operadores e incluso comillas. Entonces todo esto se puede escribir así:



Como puede ver, en esta versión, las palabras clave, espacios, comentarios, operadores, incluso comillas usan sus versiones de tamaño completo. En el caos trajo el orden.

Si Los japoneses usan VB. De hecho, a pesar de la sintaxis similar al idioma inglés (y tal vez por eso), para la mayoría de los usuarios de VB que veo en los foros, el inglés no es el idioma principal. Durante mi trabajo en Microsoft, me encontré con el MVP VB japonés varias veces, al menos uno de ellos constantemente traía dulces japoneses. Si es un programador de VB de China, Japón o Corea (o de cualquier otro país que use caracteres de ancho completo), escriba los comentarios. (En los comentarios al autor, escribieron que los japoneses están intentando en todas partes del código usar ascii, aprox. Per. )

Momento curioso: cuando inicialmente implementé líneas interpoladas en VB, (para mi vergüenza) no tomé en cuenta la posibilidad de tener llaves de ancho completo en los lugares de sustitución. Vladimir Reshetnikov ( @vreshetnikov ) descubrió y corrigió este error, por lo que la gran tradición de tolerancia VB en los anchos de caracteres seguía siendo válida.

2. VB admite citas inteligentes


Bien, esto es, por supuesto, un poco, pero digno de mención. ¿Alguna vez has visto código de muestra en un documento de texto como este:



Y después de copiar el ejemplo en su código, ¿descubrió que ninguna de las citas (resaltadas) funcionó, porque Word reemplaza todas las citas habituales de ASCII con citas inteligentes ?

Yo no Bien, lo tuve, pero solo cuando copié los ejemplos en C #. En VB, las comillas inteligentes son delimitadores válidos para cadenas (es curioso que las comillas rusas «» no funcionen - Aprox. Por):



También funcionan dentro de cadenas, aunque tal vez de una manera extraña. Si duplica las citas inteligentes para escapar, entonces todo lo que obtiene en tiempo de ejecución es solo la comilla simple ("tonta"). Esto puede parecer un poco extraño, pero sin embargo muy práctico, ya que casi cualquier otro lugar se permiten comillas inteligentes en la cadena. El compilador NO te hace terminar con una cita inteligente o usar la correcta si comenzaste con una inteligente, para que puedas mezclarla sin preocuparte. Y sí, esto también funciona con el carácter de comillas simples utilizado para comentarios:



Traté de hacer que Paul Wick ( @panopticoncntrl ) admitiera que lo hizo solo porque estaba atormentado con este problema mientras trabajaba en la especificación, pero niega cualquier culpa. Este no fue el caso en VB6, por lo que alguien agregó esto más tarde.

3. Las constantes de preprocesamiento pueden ser de cualquier tipo primitivo (incluidas las fechas) y pueden contener cualquier valor constante



4. Los operadores aritméticos se pueden usar en expresiones de preprocesamiento




Anuncios, etc.


5. VB a veces omite IL implementa declaraciones para evitar la implementación implícita accidental de una interfaz por nombre.


Este artículo pertenece a la categoría de esoterismo. En VB, la implementación de una interfaz siempre se realiza explícitamente. Pero resulta que, en ausencia de una implementación explícita, el comportamiento predeterminado del CLR cuando se llama a un método de interfaz es buscar métodos públicos por nombre y firma. En la mayoría de los casos, esto es normal, porque en VB generalmente necesita proporcionar una implementación para cada miembro de la interfaz que implemente, excepto en un caso:

 Interface IFoo Sub Bar() Sub Baz() End Interface Class Foo Implements IFoo Private Sub Bar() Implements IFoo.Bar Exit Sub End Sub Private Sub IFoo_Baz() Implements IFoo.Baz Exit Sub End Sub End Class Class FooDerived Inherits Foo Implements IFoo Public Sub Bar() Implements IFoo.Bar Exit Sub End Sub Public Sub Baz() ' Does something unrelated to what an IFoo.Baz would do. End Sub End Class 

gist.github.com/AnthonyDGreen/39634fd98a0cacc093719ab62d7ab1e6#file-partial-re-implementation-vb

En este ejemplo, la clase FooDerived solo quiere reasignar IFoo.Bar al nuevo método, pero deja las implementaciones restantes sin cambios. Resulta que si el compilador simplemente genera la directiva de implementos para FooDerived , el CLR también FooDerived.Baz como una nueva implementación de IFoo.Baz (aunque en este ejemplo no está relacionado con IFoo ). En C #, esto sucede implícitamente (y no estoy seguro si puedo rechazarlo), pero en VB, el compilador en realidad omite 'Implementos' de toda la declaración para evitar esto, y anula solo los miembros específicos que se volvieron a implementar. En otras palabras, si le pregunta a FooDerived si IFoo directamente, le dirá que no:


¿Por qué sé esto y por qué es importante? Durante muchos años, los usuarios de VB han solicitado soporte para la implementación implícita de una interfaz (sin especificar explícitamente Implements en cada declaración), generalmente para la generación de código. La incorporación de esto con la sintaxis actual sería un cambio radical, porque FooDerived.Baz ahora implementa IFoo.Baz implícitamente, aunque no ha hecho esto antes. Pero más recientemente, aprendí más sobre este comportamiento cuando discutí posibles problemas de diseño con la función de "implementación de interfaz predeterminada", que permitiría que las interfaces incluyan implementaciones predeterminadas de algunos miembros y no requieran una nueva implementación en cada clase. Esto sería útil para sobrecargas, por ejemplo, cuando es probable que la implementación sea la misma para todos los implementadores (delegación a la sobrecarga principal). Otro escenario es el versionado. Si una interfaz puede incluir implementaciones predeterminadas, puede agregarle nuevos miembros sin romper las implementaciones antiguas. Pero hay un problema. Dado que el comportamiento predeterminado en el CLR es buscar implementaciones públicas por nombre y firma, si la clase VB no implementa miembros de interfaz con implementaciones predeterminadas, pero tiene miembros públicos con un nombre y firma adecuados, implementan implícitamente estos miembros de interfaz, incluso si lo hace completamente No se supone que lo haga. Hay cosas que puede hacer para evitar esto cuando se conoce el conjunto completo de miembros de la interfaz en tiempo de compilación. Pero en caso de que el miembro se agregue después de compilar el código, solo cambia silenciosamente el comportamiento en tiempo de ejecución.

6. VB por defecto oculta a los miembros de la clase base por nombre (Shadows), y no por nombre y firma (Sobrecargas)


Creo que esta diferencia es bastante conocida. El escenario es este: usted hereda la clase base ( DomainObject ), posiblemente fuera de su control, y declara un método con un nombre que tiene sentido en el contexto de su clase, por ejemplo, Print :

 Class DomainObject End Class Class Invoice Inherits DomainObject Public Sub Print(copies As Integer) ' Sends contents of invoice to default printer. End Sub End Class 

gist.github.com/AnthonyDGreen/863cfd1e7536fe8bda7cd145795eaf9f#file-shadows-example-vb

El hecho de que se pueda imprimir una factura tiene sentido. Pero en la próxima versión de la API donde se declara su clase base, deciden la depuración para agregar a todos los DomainObjects un método que muestre el contenido completo del objeto en la ventana de depuración. Este método se llama brillantemente Imprimir. El problema es que el cliente de su API puede notar que el objeto Factura tiene métodos Print() e Print(Integer) , y piensa que se trata de sobrecargas relacionadas. Quizás el primero solo imprima una copia. Pero esto no es en absoluto lo que concibió como el autor de Factura. No tenía idea de que aparecería DomainObject.Print . Entonces sí, esto no funciona en VB. Cuando aparece esta situación, aparece una advertencia, pero lo más importante, el comportamiento predeterminado en VB es ocultar por nombre. Es decir, hasta que indique explícitamente con la palabra clave Overloads que su Print es una sobrecarga de Print clase base, un miembro de la clase base (y cualquier sobrecarga de la misma) están completamente ocultos. Solo la API que declaró originalmente se muestra a sus clientes de clase. Esto funciona de manera predeterminada, pero puede hacerlo explícitamente a través de la palabra clave Shadows. C # solo puede hacer Overloads (aunque tiene en cuenta Shadows cuando se refiere a una biblioteca VB) y lo hace de manera predeterminada (usando la new palabra clave). Pero esta diferencia aparece de vez en cuando cuando aparecen jerarquías de herencia en proyectos donde una clase se define en un idioma y otra en otro, y hay métodos sobrecargados, pero esto está más allá del alcance del elemento actual en la lista de diferencias.

7. VB11 y siguientes son más estrictos para los miembros protegidos en genéricos


De hecho, cambiamos esto entre VS2013 y VS2015. En particular, decidimos no molestarnos con la reimplementación. Pero estoy escribiendo esta distinción en caso de que use la versión anterior y lo note. En resumen: si un miembro protegido se declara en el tipo genérico, entonces el heredero, que también es genérico, puede acceder a este miembro protegido solo a través de la instancia heredada con los mismos argumentos de tipo.

 Class Base(Of T) Protected x As T End Class Class Derived(Of T) Inherits Base(Of T) Public Sub F(y As Derived(Of String)) ' Error: Derived(Of T) cannot access Derived(Of String)'s ' protected members yx = "a" End Sub End Class 

gist.github.com/AnthonyDGreen/ce12ac986219eb51d6c85fa02c339a2f#file-protected-in-generics-vb

8. La sintaxis "Argumento con nombre" en los atributos siempre inicializa propiedades / campos


VB usa la misma sintaxis := para inicializar propiedades / campos de atributo que para pasar argumentos de método por nombre. Por lo tanto, no hay forma de pasar un argumento al constructor de atributos por nombre.

9. Todas las declaraciones de nivel superior (generalmente) están ubicadas implícitamente en el espacio de nombres raíz del proyecto


Esta diferencia está casi en la categoría de "características avanzadas", pero la he incluido en la lista porque cambia el significado del código. Hay un campo en las propiedades del proyecto VB:


Por defecto, este es simplemente el nombre de su proyecto en el momento de la creación. Este NO es el mismo campo que el "Espacio de nombres predeterminado" en las propiedades de un proyecto de C #. El espacio de nombres predeterminado simplemente establece qué código se agrega por defecto a los nuevos archivos en C #. Pero el espacio de nombres raíz en VB significa que, a menos que se indique lo contrario, cada declaración de nivel superior en este proyecto está implícitamente en este espacio de nombres. Esta es la razón por la cual las plantillas de documentos VB generalmente no contienen ninguna declaración de espacio de nombres. Además, si agrega una declaración de espacio de nombres, no anula la raíz, sino que se agrega a ella:

 Namespace Controllers ' Child namespace. End Namespace Namespace Global.Controllers ' Top-level namespace End Namespace 

gist.github.com/AnthonyDGreen/fd1e5e3a58aee862a5082e1d2b078084#file-root-namespace-vb

Por lo tanto, el espacio de nombres Controllers realidad declara el espacio de nombres VBExamples.Controllers , a menos que elimine este mecanismo declarando explícitamente Global en el espacio de nombres.

Esto es conveniente porque ahorra un nivel de sangría y un concepto adicional para cada archivo VB. Y esto es especialmente útil si está creando una aplicación para UWP (porque todo debe estar en el espacio de nombres en UWP), y es extremadamente conveniente si decide cambiar el espacio de nombres de nivel superior para todo su proyecto, por ejemplo, de un nombre en clave como Roslyn a una versión más larga, como Microsoft.CodeAnalysis , ya que no tiene que actualizar manualmente cada archivo en la solución. También es importante tener esto en cuenta al trabajar con generadores de código, espacios de nombres XAML y el nuevo .vbproj archivo .vbproj .

10. Los módulos no se generan como clases abstractas selladas en IL, por lo que no se ven exactamente como clases estáticas de C # y viceversa.


Los módulos en VB existían antes de las clases estáticas de C #, aunque intentamos en 2010 hacerlos iguales en términos de IL. Desafortunadamente, este fue un cambio radical, porque el serializador XML (o tal vez era binario) para esa versión de .NET (creo que lo arreglaron) no quería serializar el tipo anidado en un tipo que no podía crearse (y una clase abstracta no puede) Lanzó una excepción.

Encontramos esto después de hacer los cambios y revertirlos porque algún código en alguna parte usaba el tipo de enumeración que estaba incrustado en el módulo. Y como no sabe con qué versión del serializador funcionará el programa compilado, nunca será posible cambiarlo, ya que en una versión de la aplicación funcionará, y en otros casos arrojará excepciones.

11. No necesita un método explícito para el punto de entrada (Sub Main) en las aplicaciones WinForms


Si su proyecto usa Formulario como el objeto de inicio y no usa el "Marco de aplicación" (más sobre esto en la próxima publicación), VB genera un Sub Main , que crea su formulario de inicio y lo pasa a Application.Run , guardando el archivo completo Para administrar este proceso, ya sea un método adicional en su Form o incluso la necesidad de pensar en este problema.

12. Si llama a algunos métodos de tiempo de ejecución VB obsoletos (por ejemplo, FileOpen), el método de llamada se marcará implícitamente con un atributo para deshabilitar la alineación por razones de corrección


En resumen, los métodos para trabajar con archivos de estilo VB6 como FileOpen basan en contextos específicos del ensamblaje donde se encuentra el código. Por ejemplo, el archivo # 1 puede ser un registro en un proyecto y una configuración en otro. Para determinar qué ensamblaje se está ejecutando, Assembly.GetCallingAssembly() llama a Assembly.GetCallingAssembly() . Pero si el JIT alinea su método con la persona que llama, desde el punto de vista de la pila, su método no llamará al método de tiempo de ejecución VB, sino a la persona que llama, que puede estar en un ensamblaje diferente, que luego puede permitir que su código acceda o viole el estado interno de la persona que llama objeto Esto no es un problema de seguridad, porque si el código comprometido se está ejecutando en su proceso, ya ha perdido. Esto es una cuestión de corrección. Por lo tanto, si utiliza estos métodos, el compilador deshabilita la inserción.

Este cambio se realizó en el último momento en 2010, porque el x64 JIT es MUY agresivo al incorporar / optimizar el código, y lo encontramos muy tarde, y era la opción más segura.

13. Si su tipo está marcado con el atributo DesignerGenerated y no contiene ninguna declaración explícita de constructor, entonces el compilador generado por defecto llamará InitializeComponent si está definido para este tipo


En la era anterior a la llegada de los tipos parciales, el equipo de VB libró una guerra para reducir el código repetitivo en los proyectos de WinForms. Pero incluso con Partial esto es útil porque permite que el archivo generado omita completamente el constructor, y el usuario puede declararlo manualmente en su archivo si es necesario, o no declararlo si no. Sin esto, el diseñador se vería obligado a agregar un constructor solo para llamar a InitializeComponent , y si el usuario también agrega, serán duplicados, o el kit de herramientas debe ser lo suficientemente inteligente como para mover el constructor del archivo del diseñador al usuario y no regenerarlo en el diseñador si ya lo ha hecho. existe en el archivo de usuario.

14. La ausencia del modificador parcial NO significa que el tipo no sea parcial


Técnicamente, en VB, solo una clase debe marcarse como Parcial. Esto suele ser (en proyectos GUI) un archivo generado.

Por qué Esto mantiene el archivo de usuario hermoso y limpio, y puede ser muy conveniente incluirlo después de generar o agregar código personalizado al código generado. Sin embargo, se recomienda que, como máximo, una clase no tenga un modificador parcial, de lo contrario se emite una advertencia.

15. En las clases predeterminadas, el nivel de acceso público es para todo excepto campos, y en estructuras públicas, también para campos


Tengo sentimientos encontrados sobre esto. En C #, todo es private de forma predeterminada (¡salud, encapsulación!), Pero hay un argumento que hacer según lo que a menudo declaras: un contrato público o detalles de implementación. Las propiedades y eventos generalmente están destinados para uso externo ( public ), y no se puede acceder a los operadores que no sean public . Sin embargo, rara vez confío en la accesibilidad predeterminada (con la excepción de demostraciones como los ejemplos en este artículo).

16. VB inicializa los campos DESPUÉS de que se llama al constructor base, mientras que C # los inicializa ANTES de que se llame al constructor base


¿Has oído cómo "algunos" dicen que lo primero que sucede en el constructor es una llamada al constructor de la clase base? Bueno, ese no es el caso, al menos en C #. En C #, antes de llamar a base() , explícito o implícito, los inicializadores de campo se ejecutan primero, luego la llamada del constructor y luego su código. Esta decisión tiene consecuencias, y creo que sé por qué los desarrolladores de idiomas podrían ir de una forma u otra. Creo que una de estas consecuencias es que el siguiente código no se puede traducir directamente a C #:

 Imports System.Reflection Class ReflectionFoo Private StringType As Type = GetType(String) Private StringLengthProperty As PropertyInfo = StringType.GetProperty("Length") Private StringGetEnumeratorMethod As MethodInfo = StringType.GetMethod("GetEnumerator") Private StringEnumeratorType As Type = StringGetEnumeratorMethod.ReturnType Sub New() Console.WriteLine(StringType) End Sub End Class 

gist.github.com/AnthonyDGreen/37d01c8e7f085e06172bfaf6a1e567d4#file-field-init-me-reference-vb

En los días en que estaba involucrado en Reflection, a menudo escribía ese código. Y recuerdo vagamente a un colega antes de Microsoft (Josh), que tradujo mi código a C #, a veces quejándose de la necesidad de portar todos mis inicializadores al constructor. En C #, está prohibido hacer referencia a un objeto que se está creando antes de llamar a base() . Y dado que los inicializadores de campo se ejecutan antes de la llamada especificada, tampoco pueden hacer referencia a otros campos ni a ningún miembro de la instancia del objeto. Entonces, este ejemplo también funciona solo en VB:

 MustInherit Class Base ' OOP OP? Private Cached As Object = DerivedFactory() Protected MustOverride Function DerivedFactory() As Object End Class Class Derived Inherits Base Protected Overrides Function DerivedFactory() As Object Return New Object() End Function End Class 

gist.github.com/AnthonyDGreen/fe5ca89e5a98efee97ffee93aa684e50#file-base-derived-init-vb

Aquí tenemos una clase base, que supuestamente tiene mucha funcionalidad, pero necesita algún objeto clave para la administración, el trabajo, que está determinado por el tipo derivado. Hay muchas formas de implementar dicha plantilla, pero generalmente uso esta porque:

  • él es bajo;
  • no requiere que declare un constructor;
  • no requiere que coloque el código de inicialización en el constructor, si lo hay;
  • me permite almacenar en caché el objeto creado y no requiere tipos derivados para declarar y administrar el almacenamiento del objeto proporcionado, aunque ahora esto no es un problema con las propiedades automáticas.

Además, he estado en ambas situaciones: cuando un campo en un tipo derivado quería llamar a un método declarado en la clase base, y cuando el inicializador de la clase de campo necesitaba llamar al miembro MustOverride implementado por el tipo derivado. Ambos son válidos en VB y ninguno en C #, y eso tiene sentido. Si el inicializador de campo C # pudiera llamar a un miembro de la clase base, este miembro podría depender de los campos inicializados en el constructor base (que aún no se está ejecutando) y los resultados seguramente serían incorrectos, y no había forma de evitarlo.

Pero en VB, el constructor base ya ha funcionado, ¡así que puedes hacer cualquier cosa! En la situación opuesta, todo es un poco más complicado, porque llamar al miembro Overridable desde el inicializador (o constructor) de la clase base puede conducir al acceso a los campos antes de que se "inicialicen". Pero solo su implementación sabe si esto es un problema. En mis guiones, esto simplemente no sucede. No dependen del estado de la instancia, pero no pueden ser miembros Shared porque no puede tener un miembro Shared Overridable en ningún idioma por razones técnicas más allá del alcance de este artículo. Además, está claramente definido qué sucede con los campos antes de iniciar los inicializadores personalizados: se inicializan con valores predeterminados, como todas las variables en VB. Sin sorpresas

¿Entonces por qué? En realidad, no sé si mis scripts fueron lo que el equipo original de VB.NET tenía en mente cuando lo diseñaron. ¡Simplemente funciona en mi caso! , : VB , , . . .

, , , C# VB, , VB , C#.

17. (backing field) VB , C#,


( ). E , VB ( IDE) EEvent . C# E , , E , .

18. VB


P , _P' . IntelliSense, . C# «» ( mangled ) , , C# .

? VB , -, «WithEvents», -, , - , .

19. read-only


, , …. VB « » . WithEvents -, non-Custom , , . IntelliSense, , , . FTW! , VB , private set; C#.

 Class Alarm Private ReadOnly Code As Integer ReadOnly Property Status As String = "Disarmed" Sub New(code As Integer) Me.Code = code End Sub Sub Arm() ' I'm motifying the value of this externally read-only property here. _Status = "Armed" End Sub Function Disarm(code As Integer) As Boolean If code = Me.Code Then ' And here. _Status = "Disarmed" Return True Else Return False End If End Function End Class 

gist.github.com/AnthonyDGreen/57ce7962700c5498894ad417296f9066#file-read-only-auto-property-backing-field-is-writeable-vb

20.


, NonSerialized .

VB (expanded) Custom- 2005 (?) , , , NonSerialized . , , , , «» , « ».

, , , , . , , , , two-way bindable ( , PropertyChanged ), , , , , .

, , CLSA «Expert Business Objects» (Rocky Lhotka) , undo/redo ( , , - , , ), . , . , , , .


21. — , ; ( )


, GoTo . , - . , For For Each ; Using , SyncLock With , , , Finally . If Select Case , Do While , Try — , :

 Module Program Sub Main() Dim retryCount = 0 Try Retry: ' IO call. Catch ex As IO.IOException When retryCount < 3 retryCount += 1 GoTo Retry End Try End Sub End Module 

gist.github.com/AnthonyDGreen/b93adcf3c3705e4768dcab0b05b187a0#file-try-goto-retry-vb

, , , .NET VB «». VB6 Quick Basic ( ) . QB, . , « », . GoTo, — , .

: Try , VB - await Catch Finally , , GoTo .

22. <>


, VB ( ) ( static ) ( ). , . Catch 3 . Try Catch , , , Try .

, VB.NET , . CLR VB . : , .

, C# , , «». VB.NET .

23.


, , C# « » ( definite assignment ). , , , « ». , ( ) , , . C/C++. , ! , . , , , — . , , , , , , , , . , BASIC , , «» , = Nothing , = 0 , = False ..

, ( flow analysis ) VB , .

, C# , , , . VB , , , . Roslyn, , API « », , .

24. RaiseEvent , null


, - C# VB. RaiseEvent VB — , null ( ), null - — , .

 ' You don't have to write this: If PropertyChangedEvent IsNot Nothing Then RaiseEvent PropertyChanged(Me, e) End If ' You don't have to write this: Dim handlers = PropertyChangedEvent If handlers IsNot Nothing Then handlers(Me, e) End If ' You don't have to write this either: PropertyChangedEvent?(Me, e) ' Just write this: RaiseEvent PropertyChanged(Me, e) 

gist.github.com/AnthonyDGreen/c3dea3d91ef4ffc50cfa92c41f967937#file-null-safe-event-raising-vb

, null-conditional C# VS2015 C# , VB ( ), , ; VB.NET .

25. ; (shallow clone)


, , 17 , , . (boxed) Object, System.Runtime.CompilerServices.RuntimeHelper.GetObjectValue . , CLR. , :

  • , .
  • , (, Integer ), .
  • , .

, , , , ( late-bound situations ). , , ( ) , , , , ( caller's copy ). , , - , — .

. :

 Class MyEventArgs Property Value As Object End Class Structure MyStruct Public X, Y As Integer End Structure Module Program Sub Main() Dim defaultValue As Object = New MyStruct With {.X = 3, .Y = 5} Dim e = New MyEventArgs With {.Value = defaultValue} RaiseEvent DoSomething(Nothing, e) If e.Value Is defaultValue Then ' No handlers have changed anything. Console.WriteLine("Unchanged.") End If End Sub Event DoSomething(sender As Object, e As MyEventArgs) End Module 

gist.github.com/AnthonyDGreen/422ac4574af92d9bbbf59f0fbc40b74d#file-get-object-value-vb

, WPF, . , . , , . , . , - , , , .

, , « » . IronRuby/Python, dynamic C# ( C#): C# GetObjectValue . object.ReferenceEquals , , , - dynamic C# ( ). == , . C#, , .

26. Select Case «» (fall-through); break


Friday , Sunday — , 5 .

 Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday: Case DayOfWeek.Tuesday: Case DayOfWeek.Wednesday: Case DayOfWeek.Thursday: Case DayOfWeek.Friday: Console.WriteLine("Weekday") Case DayOfWeek.Saturday: Case DayOfWeek.Sunday: Console.WriteLine("Weekend") End Select End Sub End Module 

gist.github.com/AnthonyDGreen/7b7e136c71dd11b2417a6c7267bb3546#file-select-case-no-fallthrough-vb

Roslyn C# , - : «, ? !» «, » . . VS , , , , , . !

. C# , C, C. . , C# , case . - , goto , break . VB break , Exit Select , , VB .

27. Case


, . C#, :

 Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday Dim message = "Get to work!" Case DayOfWeek.Saturday, DayOfWeek.Sunday Dim message = "Funtime!" End Select End Sub End Module 

gist.github.com/AnthonyDGreen/bd642061896246c9336255881fb78546#file-select-case-scopes-vb

, message , C# switch case — . . , , - ( , C): , , , , .

28, 29, 30. Select Case , =


, , , , Select Case .

, , . :

  • Select Case — , , …
  • switch — / , « ».

, 26-30. switch , , , , if . IL switch , , If , VB , . switch , , C . VB .

31. , ,


x , , -1, -2, -3:

 Module Program Sub Main() For i = 1 To 3 Dim x As Integer x -= 1 Console.WriteLine(x) Next End Sub End Module 

gist.github.com/AnthonyDGreen/cbc3a9c70677354973d64f1d993a3c5d#file-loop-variables-retain-their-values-vb

« , , » ( ). , VB2008 , -:

 Module Program Sub Main() Dim lambdas = New List(Of Action) For i = 1 To 3 Dim x As Integer x -= 1 lambdas.Add(Sub() Console.WriteLine(x)) Next For Each lambda In lambdas lambda() Next End Sub End Module 

gist.github.com/AnthonyDGreen/2ef9ba3dfcf9a1abe0e94b0cde12faf1#file-loop-variables-captured-per-iteration-vb

-1, -2, -3. x — « », - x , . , x . flow analysis API — ! ( «… … ?» )

? , , , , , #22. , , -, .

, VB C# ( control variables ) For Each VS2012 (?), - « ». 10000% , ( , VB , ). , VB For , . , . , VB For For Each , for foreach C#. , For VB - , , .

32. For


For . , , 1,3,5,7,9, , .

 Module Program Sub Main() Dim lower = 1, upper = 9, increment = 2 For i = lower To upper Step increment Console.WriteLine(i) upper += 1 increment -= 1 Next End Sub End Module 

gist.github.com/AnthonyDGreen/1e48113be204f515c51e221858666ac7#file-for-loop-bounds-cached-vb

, ( ), , , , IndexOutOfRangeExceptions , .

, , , , , C, VB . - , VB , For i = a To b Step c ( , i> b ) ( , i <b ), c ? , , , b , — . , , , , .

33. For Each VB GetEnumerator


For Each , IEnumerable , GetEnumerator , For Each .
, , For Each IEnumerator , , :

 Module Program Sub Main() Dim list = New List(Of Integer) From {1, 2, 3, 4, 5} Dim info = list.FirstAndRest() If info.First IsNot Nothing Then Console.Write(info.First.GetValueOrDefault()) For Each other In info.Additional Console.Write(", ") Console.Write(other) Next Console.WriteLine() End If End Sub <Runtime.CompilerServices.Extension> Function FirstAndRest(Of T As Structure)(sequence As IEnumerable(Of T)) As (First As T?, Additional As IEnumerator(Of T)) Dim enumerator = sequence.GetEnumerator() If enumerator.MoveNext() Then Return (enumerator.Current, enumerator) Else Return (Nothing, enumerator) End If End Function <Runtime.CompilerServices.Extension> Function GetEnumerator(Of T)(enumerator As IEnumerator(Of T)) As IEnumerator(Of T) Return enumerator End Function End Module 

gist.github.com/AnthonyDGreen/d7dbb7a5b98a940765c4adc33e3eaeee#file-for-each-extension-get-enumerator-vb

F# , IEnumerator , For Each , .

VB , ( well-known name ), . , , Add, . C# , (. async / await ). , C# Roslyn () , .
Minuto de publicidad. 15-16 - .NET- DotNext 2019 Piter . , . , . .

Source: https://habr.com/ru/post/443684/


All Articles