Comparación de C Sharp y Java

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 7 de octubre de 2019; las comprobaciones requieren 46 ediciones .

C# y Java  son dos lenguajes de programación que desarrollan el lenguaje de programación C++ , con una sintaxis que hereda en gran medida la sintaxis de C++ , y creados en muchos aspectos en un entorno competitivo, y, como resultado, tienen ciertas similitudes , además de tener una serie de diferencias .

Vista general

Los lenguajes C# y Java aparecieron en diferentes momentos. El lenguaje Java se creó mucho antes de la llegada de C#. Oak Java fue desarrollado por Sun Microsystems en 1990 y en 1995 se lanzó la primera versión beta de Java. La creación de C# se anunció en 2000 y en 2002 se lanzó la primera versión de la plataforma .NET compatible con C#. Por lo tanto, si Java se creó apoyándose más en la experiencia de los lenguajes Objective C y C, entonces para C#, C++ y el mismo Java fueron un soporte de este tipo [1] . Y, a pesar de su nombre, C# resultó estar más cerca de Java que de C++ [2] [3] .

Desde el punto de vista de un desarrollador, Java y C# son muy similares. Ambos lenguajes están fuertemente tipados, lenguajes objeto. Ambos han incorporado gran parte de la sintaxis de C++, pero a diferencia de C++, son más fáciles de aprender para los principiantes. Ambos tomaron prestado de C un conjunto de palabras clave básicas y símbolos de servicio, incluidas llaves para resaltar bloques. Ambos lenguajes se basan en la recolección de basura . Ambos idiomas van acompañados de ricas colecciones de bibliotecas. Pero los idiomas también tienen sus propias características y diferencias, fortalezas y debilidades. C# tuvo en cuenta muchas de las deficiencias de Java y las corrigió en su implementación [4] . Pero Java no se detiene, desarrollándose en paralelo con C#.

Kik Redek de Microsoft considera que C# es un lenguaje más complejo que Java [1] . En su opinión, " Java se creó para evitar que un  desarrollador se dispare en el pie" , y "C# se creó para darle al desarrollador un arma, pero dejar el seguro activado" ( inglés  "C# se creó para darle al desarrollador una pistola pero deje el seguro activado" ).

Idioma

Sintaxis

Las diferencias sintácticas también son suficientes.

Sintaxis Java C#
Importar nombres estáticos
( import static)
le permite importar por separado algunos o todos los métodos estáticos y variables de una clase y usar sus nombres sin calificación en el módulo de importación Desde C# 6.0 esto se ha introducido (p. ej. ( using static System.Math)).
declaración de cambio El argumento de la declaración de cambio debe ser un número entero o un tipo enumerado. A partir de Java 7, se hizo posible usar cadenas literales en una declaración de cambio, y esta distinción de C# se eliminó [2] . Se admiten tanto tipos constantes como tipos de cadena. C# 7 introdujo soporte para tipos de referencia y null. También es posible especificar condiciones adicionales para un bloque utilizando la casepalabra clave when[5] . A diferencia de Java, no hay una transición directa al siguiente bloque case. Para pasar al siguiente bloque case, debe usar la declaración goto [2] .
instrucción goto jump el uso de goto se abandonó deliberadamente, sin embargo, hay un mecanismo que le permite salir del bucle externo del anidado marcándolo con la etiqueta y usando los operadores break, continuejunto con la etiqueta ( continue <метка>;) goto se conserva, su uso habitual es transferir el control a diferentes etiquetas caseen la declaración switchy salir del ciclo anidado
constantes no hay constantes como tales, en su lugar se usan variables de clase estáticas con un modificador final ; el efecto de su uso es exactamente el mismo concepto separado de constante tipeada con nombre y palabra claveconst
Precisión de punto flotante Java contiene la construcción strictfp , que garantiza los mismos resultados para las operaciones de coma flotante en todas las plataformas. C# se basa en la implementación, no hay garantía de obtener exactamente los mismos resultados de los cálculos.
Deshabilitar cheques En Java, todas las comprobaciones dinámicas están habilitadas/deshabilitadas solo a nivel de paquete C# contiene las construcciones y checkedpara uncheckedhabilitar o deshabilitar la verificación de desbordamiento aritmético dinámico localmente .


Mecanismo de datos dinámicos y recolección de basura

Ambos lenguajes implementan el mismo modelo para trabajar con datos dinámicos: los objetos se crean dinámicamente utilizando la construcción new, el tiempo de ejecución supervisa las referencias a ellos y el recolector de elementos no utilizados limpia periódicamente la memoria de los objetos a los que no se hace referencia. Para optimizar la recolección de basura, las especificaciones de los lenguajes y los entornos de tiempo de ejecución no contienen restricciones sobre la vida útil de un objeto después de que se eliminó la última referencia: el recolector funciona independientemente de la ejecución del programa, por lo que la destrucción real de el objeto puede aparecer en cualquier momento después de que se elimine la última referencia antes de que finalice el programa. En realidad, los recolectores de basura optimizan la ejecución de tal manera que proporcionan un consumo de memoria aceptable con una ralentización mínima de los programas.

Tanto Java como C# tienen referencias de objetos fuertes y débiles . Ambos idiomas admiten métodos de finalización . Debido a la incertidumbre de cuándo se elimina un objeto, los finalizadores no se pueden usar para liberar los recursos del sistema ocupados por el objeto, lo que lo obliga a crear métodos adicionales para "limpiar" el objeto y llamarlos explícitamente.

C# tiene una interfaz en la biblioteca estándar IDisposabley una construcción especial usingpara garantizar que el método de limpieza se llame a tiempo:

//DisposableClass implementa la interfaz IDisposable y describe su método Dispose class desechableClass : IDisposable { public void Dispose () { // ... Los recursos ocupados por la instancia se liberan aquí } } usando ( ClaseDisponible obj = nueva ClaseDesechable (...)) { // ... Código que usa el objeto obj } // ... Aquí, se garantiza que el método Dispose ya se ha llamado en el objeto obj

No existe tal construcción en Java, y la limpieza de objetos solo se puede realizar manualmente:

class AnyClass { void clear () { // ... El código claro va aquí } } CualquierClase obj = new CualquierClase (...); intente { // ... código usando obj } finalmente { obj . claro (); // - una llamada explícita al método de limpieza del objeto después de que se complete su uso }

Java 7 agregó una construcción de "probar con recursos" para proporcionar una limpieza automática exactamente de la misma manera que C#:

try ( BufferedReader br = new BufferedReader ( nuevo FileReader ( ruta ))) { return br . leerLínea (); }

Cuando el bloque de prueba sale, todos los objetos a los que se les ha asignado un valor en su encabezado (los paréntesis antes del bloque de declaración) se borrarán. Un requisito previo es que las clases de estos objetos deben implementar la interfaz del sistema java.lang.AutoCloseable.

Java le permite registrar un oyente que recibirá mensajes cuando una referencia se recopile como basura, lo que mejora el rendimiento de WeakHashMap .

C# (más específicamente, Common Language Runtime) le permite cancelar la ejecución de un finalizador para un objeto determinado mediante un método GC.SuppressFinalize(obj)(por ejemplo, una conexión SQL en un flujo de archivos). Esto es útil porque la finalización se considera una operación de recolección de elementos no utilizados relativamente costosa y un objeto con un finalizador "vive" más tiempo.

Instalaciones de objetos

Ambos lenguajes están orientados a objetos , con una sintaxis heredada de C++ pero significativamente rediseñada. El código y los datos solo se pueden describir dentro de las clases.

Encapsulación

En Java, el modificador protected en la declaración, además del acceso desde las clases descendientes, permite el acceso desde todas las clases en el mismo paquete que la clase propietaria.

En C#, para los objetos que deberían estar visibles dentro del ensamblado (un análogo aproximado del paquete de Java), se introdujo un modificador interno separado (un análogo del valor predeterminado en Java), y protected conserva su significado original, tomado de C ++ - acceso solo desde clases descendientes. Se permite combinar interno y protegido; luego obtiene el área de acceso correspondiente a protegido en Java.

Clases internas

Ambos lenguajes te permiten definir una clase dentro de una clase.

En Java, las clases internas se utilizan para emular cierres. Las clases internas de Java tienen acceso a los miembros no estáticos de la clase principal, es decir, "saber sobre esto"; Además, dentro de los métodos, puede definir clases locales que tienen acceso de lectura a variables locales y clases locales sin nombre (anónimas), que en realidad le permiten crear instancias de objetos e interfaces que anulan los métodos de su clase, directamente en el lugar. de su uso. El manejo de eventos se puede construir sobre este mecanismo en programas Java (un evento genera una llamada a un método que es abstracto en la clase de controlador original; cuando se necesita un controlador de eventos específico, el programador crea una instancia de una clase anónima local: el sucesor de la clase de controlador base y la usa directamente). Esto elimina la necesidad de un tipo especial y soporte sintáctico para eventos, pero el código real que crea los controladores es algo más complejo de entender. En particular, los ámbitos variables se vuelven más complejos .

C# tiene cierres y lambdas. El enfoque de C# es más similar a C++: las clases internas en C# solo tienen acceso a los miembros estáticos de la clase externa y, para acceder a los miembros no estáticos, debe especificar explícitamente una instancia de la clase externa. Las clases internas locales no se admiten en C#.

Las expresiones lambda también han aparecido en Java desde la versión 8 .

Métodos

En ambos lenguajes, los métodos se definen a través de funciones de clase. El cuerpo del método se encuentra dentro de la declaración de la clase. Se admiten métodos estáticos, métodos abstractos . C# también tiene una implementación explícita de métodos de interfaz, lo que permite que una clase implemente métodos de interfaz por separado de sus propios métodos, o proporcione diferentes implementaciones de métodos del mismo nombre que pertenecen a dos interfaces diferentes.

Java 8 introdujo la declaración predeterminada, que le permite definir la implementación "predeterminada" de los métodos de interfaz. Por lo tanto, la clase que implementa la interfaz se libera de la obligación de implementar métodos predeterminados, pero puede anularlos.

Una característica similar apareció en C # 9.0: dentro del marco de la descripción de la interfaz, el desarrollador puede especificar cuerpos para cualquier método. Al igual que en Java, al implementar una interfaz, el desarrollador puede anular la implementación predeterminada, pero no está obligado a hacerlo.

En Java, los tipos primitivos ( byte, int, double, float, booleanetc.) se pasan por valor, y para el resto de tipos (objetos), se pasa una referencia de objeto por valor.

En C#, además de los tipos primitivos se pasan por valor de estructura ( struct) (los llamados tipos de valor), otros tipos se pasan por referencia (los llamados tipos de referencia). C# también admite la descripción explícita del paso de parámetros por referencia ( las palabras clave refy ). Al usar out, el compilador controla si el valor está presente en el método de asignación. También se admite devolver valores de métodos por referencia utilizando la construcción ref typename. inout

C# permite que los métodos reciban el mismo nombre que el nombre de la clase, creando así un constructor de clase [6] (en Java, un programador también puede definir un constructor que en realidad será un método) [4] .

C# admite varios subtipos de sintaxis especiales al describir cuerpos de métodos:

- bloques iteradores: métodos que devuelven IEnumerable<T>o IEnumerator<T>pueden describir imperativamente la secuencia de valores devueltos utilizando las construcciones yield returny yield break.

- métodos asincrónicos : los métodos que devuelven Task// ValueTasky están marcados con la palabra clave pueden usar la construcción await en sus cuerpos al llamar a otros métodos que devuelven // . Esto le permite implementar la multitarea cooperativa, porque. la construcción await interrumpe la ejecución del método actual hasta que el valor solicitado esté listo y transfiere el control al programador, que puede comenzar a ejecutar la siguiente tarea lista sin transferir el control al kernel del sistema operativo. Task<T>ValueTask<T>asyncTaskValueTaskTask<T>ValueTask<T>

A partir de C# 8.0, puede combinar ambas funciones mediante la generación de iteradores asíncronos, una implementación de las interfaces IAsyncEnumerable<T>/ IAsyncEnumerator<T>mediante el uso de await, yield returny construcciones yield break.

Virtualidad de métodos

C# copia el concepto de métodos virtuales de C++ : un método virtual debe declararse explícitamente con la palabra clave virtual, otros métodos no son virtuales. Esta declaración selectiva de métodos virtuales se introdujo en C#, ya que declarar todos los métodos virtuales puede ralentizar mucho la ejecución [7] . Además, C# requiere una declaración explícita de un reemplazo de método virtual en una clase derivada con la palabra clave override. Si desea ocultar (ocultar) un método virtual, es decir, simplemente introducir un nuevo método con el mismo nombre y firma, debe especificar una palabra clave new(en ausencia de la cual el compilador emite una advertencia). Está prohibido ocultar (oscurecer) métodos abstractos. Declarar un método de anulación con la palabra clave sealedevita que el método de anulación se anule en las clases descendientes, pero aún le permite ocultarlo.

En Java, por el contrario, todos los métodos públicos, excepto los estáticos, son virtuales y es imposible anular un método para que el mecanismo de virtualidad no se active. El método siempre anula virtualmente el método de la clase base con el mismo nombre y firma, si corresponde. La palabra clave finalle permite evitar la creación de un método con la misma firma en clases derivadas.

El enfoque de Java es sintácticamente más simple y asegura que siempre se invoque el método de la clase a la que pertenece el objeto. Por otro lado, la virtualidad no siempre es necesaria, y la sobrecarga de llamar a métodos virtuales es algo mayor, ya que estas llamadas generalmente no pasan por sustitución en línea y requieren acceso adicional a la tabla de métodos virtuales (aunque algunas implementaciones de JVM, incluido la implementación de Sun, implementa la sustitución en línea de los métodos virtuales llamados con más frecuencia).

La virtualidad de todos los métodos es potencialmente insegura: si un programador declara por error un método que ya está en la clase base, sin tener la intención de anularlo, sino simplemente sin prestar atención al hecho de que dicho método ya existe, entonces el nuevo El método anulará el método del mismo nombre en la clase base, aunque esta no es la intención del desarrollador. En C#, también es posible un error similar, pero el compilador emitirá una advertencia de que el método de anulación se declara sin newy override. Java 5 introdujo un mecanismo similar: si un método anula un método virtual de una clase antepasada, el compilador emite una advertencia; Para evitar que se emita una advertencia, el método de anulación debe anotarse con la anotación "@Override".

Tipos de datos

Tipos primitivos

Ambos lenguajes soportan la idea de tipos primitivos (que en C# son un subconjunto de tipos de valor  - tipos de valor ), y tanto para la traducción de tipos primitivos en tipos de objetos proporcionan su "encajonamiento" automático en objetos (boxing) y "unboxing" (unboxing) (en Java - a partir de la versión 5). En C#, los tipos primitivos se pueden denominar objetos y esta es una de las razones por las que C# es popular. En Java, los tipos primitivos y los tipos de objetos están separados, las clases contenedoras se usan para referirse a los tipos primitivos como objetos (por ejemplo, el contenedor Integer para el tipo int), esto causa insatisfacción con muchos desarrolladores de Java [8] [9] .

Hay más tipos primitivos en C# que en Java, debido a los tipos enteros sin signo (unsigned), que se emparejan con todos los firmados, y un tipo especial decimalpara cálculos de punto fijo de alta precisión (en Java, las clases java.math.BigIntegery sirven para esto java.math.BigDecimal).

Java ha abandonado la mayoría de los tipos sin firmar para simplificar el lenguaje. Uno de los problemas bien conocidos con tales tipos es la dificultad para determinar el tipo del resultado de operaciones aritméticas en dos argumentos, uno de los cuales tiene signo y el otro no tiene signo. Independientemente de las reglas que adopte el lenguaje con respecto a tales operaciones, en algunas situaciones dará lugar a errores (por ejemplo, en C ++, una operación en un valor con signo y sin signo da un resultado sin signo; como resultado, al contar con 16- números de bits, la expresión "40000 / (-4)" dará como resultado no -10000, sino 55536). Sin embargo, esta negativa crea sus propios problemas; ya que una parte significativa de los datos técnicos utilizados a bajo nivel (por ejemplo, varios datos de servicio transmitidos por el hardware y devueltos por las funciones API del sistema operativo) son del tipo entero sin signo, y la ausencia de tales tipos lleva a la necesidad para realizar operaciones de conversión de datos no seguras y, en algunos casos, reemplazar el uso de aritmética simple sin signo con combinaciones no obvias de operaciones bit a bit.

Estructuras (registros)

C# le permite crear tipos de valores personalizados utilizando struct. Este es un legado directo del lenguaje C ++, que los creadores de Java abandonaron deliberadamente. A diferencia de las instancias de clase, las instancias de tipo de valor no se crean en el montón sino en la pila de llamadas o como parte de la instancia del objeto en el que se declaran, lo que en algunos casos mejora el rendimiento del código. Desde el punto de vista de un programador, son similares a las clases, pero con varias limitaciones: no pueden tener un constructor sin parámetros explícito (pero pueden tener un constructor con parámetros), no se pueden heredar de [10] y no se pueden heredar explícitamente de otros tipos (siempre implícitamente), se heredan de la clase System.ValueType), pero pueden implementar interfaces. Además, los valores de los tipos de estructuras admiten la lógica de asignar un valor (es decir, asignar el valor de una variable a otra no copia la referencia al mismo objeto, pero copia los valores de campo de una estructura a otro). Desde la versión 1.6, Java también tiene la capacidad de crear objetos en la pila, pero esto sucede automáticamente sin la intervención del usuario.

En Java, para evitar que una clase se herede, se puede declarar final final, obteniendo así un análogo parcial de la construcción struct(de todos modos, no se admitirá la copia por valor). sealedC# usa el modificador [11] para el mismo propósito .

Tipos enumerados

Los tipos enumerados en C# se derivan de tipos integrales primitivos. Un valor válido de un tipo enumerado es cualquier valor de su primitiva subyacente, aunque su asignación puede requerir una conversión explícita . Esto permite que los valores de tipo enumerados se combinen con una operación bit a bit "o" si son indicadores de bits.

En Java, los tipos enumerados son clases y sus valores son, respectivamente, objetos. Un tipo de enumeración puede tener métodos, implementar interfaces. Los únicos valores de tipo válidos son los que figuran en la declaración. Combinarlos juntos como banderas requiere un objeto de conjunto de enumeración especial. Es posible definir diferentes implementaciones de métodos para cada valor.

Matrices y colecciones

Las matrices y las colecciones también recibieron expresión en la sintaxis de ambos lenguajes, gracias a un tipo especial de bucle for (bucle sobre una colección, también conocido como bucle for foreach). En ambos lenguajes, una matriz es un objeto de clase Array, pero en Java no implementa ninguna interfaz de colección, aunque es posible iterar sobre matrices con un bucle for(:). Ambos lenguajes tienen clases de colección genéricas en la biblioteca estándar .

En Java, estrictamente hablando, solo se pueden declarar matrices unidimensionales. Una matriz multidimensional en Java es una matriz de matrices. C# tiene verdaderos arreglos multidimensionales y arreglos de arreglos, a los que comúnmente se hace referencia en C# como arreglos "irregulares" o "irregulares". Las matrices multidimensionales siempre son "rectangulares" (en términos 2D), mientras que las matrices de matrices pueden almacenar cadenas de diferentes longitudes (nuevamente en 2D, de manera similar en multidimensional). Los arreglos multidimensionales aceleran el acceso a la memoria (para ellos, el puntero se desreferencia solo una vez), mientras que los arreglos irregulares son más lentos, pero ahorran memoria cuando no todas las filas están llenas. Las matrices multidimensionales requieren solo una llamada del operador para crearlas new, mientras que las matrices irregulares requieren una asignación explícita de memoria en un bucle para cada fila.

Tipos parametrizados (genéricos)

En ambos lenguajes, los tipos se pueden parametrizar, lo que admite el paradigma de programación genérico . Sintácticamente, la definición de tipos es bastante cercana: en ambos idiomas se hereda de las plantillas de C ++, aunque con algunas modificaciones.

Los genéricos en Java son puramente una construcción de lenguaje y se implementan solo en el compilador. El compilador reemplaza todos los tipos genéricos con sus límites superiores e inserta la conversión apropiada donde se usa el tipo parametrizado. El resultado es un código de bytes que no contiene referencias a tipos genéricos y sus parámetros. Esta técnica para implementar tipos genéricos se denomina borrado de tipos . Esto significa que la información sobre los tipos genéricos originales no está disponible en tiempo de ejecución e impone algunas restricciones, como la incapacidad de crear nuevas instancias de matriz a partir de argumentos de tipo genérico. El entorno de tiempo de ejecución de Java no está familiarizado con el sistema de tipo genérico, lo que da como resultado nuevas implementaciones de JVM que solo requieren actualizaciones mínimas para trabajar con el nuevo formato de clase.

C # fue por el otro lado. El soporte de Genericity se ha integrado en el tiempo de ejecución virtual mismo, introducido por primera vez en .NET 2.0. El lenguaje aquí se ha convertido solo en una interfaz externa para acceder a estas características del entorno. Al igual que en Java, el compilador realiza una verificación de tipo estático, pero además de esto, el JIT realiza una validación de tiempo de carga . La información de tipo genérico está completamente presente en el tiempo de ejecución y permite el soporte completo para la reflexión de tipo genérico y la creación de nuevas implementaciones.

El enfoque de Java requiere comprobaciones de tiempo de ejecución adicionales, no garantiza que el cliente de código siga la coincidencia de tipos y no proporciona reflejo para los tipos genéricos. Java no permite que los genéricos se especialicen en primitivos (esto solo se puede hacer envolviendo tipos primitivos en clases), mientras que C# proporciona genéricos tanto para tipos de referencia como para tipos de valor, incluidos los primitivos. En su lugar, Java sugiere usar tipos primitivos envueltos como parámetros (por ejemplo , List<Integer>en lugar List<int>de ), pero esto tiene el costo de una asignación de montón adicional. Tanto en Java como en C#, las especializaciones de tipo genérico en diferentes tipos de referencia producen el mismo código [12] , pero para C#, el tiempo de ejecución genera dinámicamente código optimizado cuando se especializa en tipos de valor (p . ej. List<int>), lo que permite almacenarlos y recuperarlos de contenedores sin operaciones para- y despliegue.

Gestión de eventos

Java requiere que el programador implemente manualmente el patrón de observador , aunque proporciona algo de azúcar sintáctico en forma de clases anidadas anónimas , lo que le permite definir un cuerpo de clase e instanciarlo inmediatamente en un punto del código.

C# proporciona una amplia compatibilidad con la programación basada en eventos a nivel de lenguaje, incluidos delegados de .NET , multidifusión, sintaxis especial para establecer eventos en clases, operaciones para registrar y cancelar el registro de controladores de eventos, covarianza de delegado y métodos anónimos con un conjunto completo de semántica de cierre . .

Los cierres están incluidos en Java SE 8 [1] . Estos cierres, como los delegados en C#, tienen acceso total a todas las variables locales en un ámbito determinado, no solo acceso de lectura a las variables marcadas con una palabra final(como con las clases anidadas anónimas).

Sobrecarga de operadores

C# incluye la sobrecarga de operadores y la conversión de tipos especificados por el usuario que son familiares para los programadores de C++. C# lo admite con algunas restricciones que aseguran la integridad lógica que, cuando se usa con cuidado, ayuda a que el código sea más conciso y legible. Java no incluye la sobrecarga de operadores para evitar abusos y mantener el lenguaje simple [13] [14] [15] .

Propiedades

C# admite el concepto de "propiedades": pseudocampos de una clase, a los que se proporciona acceso con control total mediante la creación de métodos para recuperar y establecer el valor del campo. Las descripciones de propiedades se realizan utilizando las construcciones gety set. No existe tal concepto en Java [16] (aunque no hay restricciones para implementarlo usando métodos tradicionales).

C# también incluye los llamados indexadores , que se pueden considerar como un caso especial de sobrecarga de operadores (similar a la sobrecarga operator[]en C++) o propiedades parametrizadas. Un indexador es una propiedad denominada this[], que puede tener uno o más parámetros (índices), y los índices pueden ser de cualquier tipo. Esto le permite crear clases cuyas instancias se comportan como arreglos/mapas:

miLista [ 4 ] = 5 ; cadena nombre = xmlNode . Atributos [ "nombre" ]; pedidos = mapaCliente [ elCliente ];

El uso de propiedades está mal visto por algunos programadores autorizados. En particular, Jeffrey Richter escribe:

“Personalmente, no me gustan las propiedades y me encantaría que se eliminara su compatibilidad con Microsoft .NET Framework y los lenguajes de programación relacionados. La razón es que las propiedades parecen campos, pero en realidad son métodos". [17]

De acuerdo con el estilo de nomenclatura común de C#, los nombres de propiedad se distinguen visualmente de los campos porque comienzan con una letra mayúscula.

Compilación condicional

C#, a diferencia de Java, admite la compilación condicional mediante directivas de preprocesador . También tiene un atributo Conditionalque significa que el método especificado se llama solo cuando se define la constante de compilación dada. De esta manera, puede insertar en el código, por ejemplo, comprobaciones de aserción, que solo funcionarán en la versión de depuración cuando se defina la constante DEBUG. En la biblioteca estándar de .NET, este es el Debug.Assert().

Las versiones de Java 1.4 y posteriores incluyen un verificador de suposiciones en tiempo de ejecución en el lenguaje. Además, si las construcciones con condiciones constantes se pueden expandir en tiempo de compilación. Existen implementaciones de terceros de preprocesadores para Java, se utilizan principalmente en el desarrollo de aplicaciones para dispositivos móviles.

Espacios de nombres, ensamblajes, paquetes

Los módulos externos en Java y C# se conectan de manera similar. Java usa la palabra clave import, C# usa la palabra clave using. Ejemplo [18] :

// Ejemplo de Java importar java.lang.System ; public class GlobalGreeting2 { public static void main ( String [] args ) { System . fuera _ println ( "Zdravo, zemjata!" ); } } // Ejemplo de C# usando System ; public class GlobalGreeting2 { public static void Main ( string [] args ) { Console . WriteLine ( "¡Saludos, mundo!" ); } }

La diferencia esencial entre importar en Java y usar en C# es que C# utiliza el concepto de espacios de nombres (namespace), que recuerda al mecanismo de C++ del mismo nombre [18] . Java utiliza el concepto de paquetes . Los espacios de nombres no tienen nada que ver con los módulos compilados (ensamblajes o ensamblaje en la terminología de Microsoft). Varios ensamblados pueden contener el mismo espacio de nombres y un ensamblado puede declarar varios espacios de nombres, no necesariamente anidados. Los modificadores de alcance de C# no tienen nada que ver con los espacios de nombres. En Java, las clases declaradas en el mismo paquete por defecto forman una sola unidad compilada. El modificador de alcance predeterminado (sin especificación explícita) restringe el alcance de los campos y métodos de clase al paquete.

En Java, la estructura de los archivos y directorios de origen de un paquete está relacionada de forma predeterminada con la estructura del paquete: un paquete corresponde a un directorio, sus subpaquetes: subdirectorios de este directorio, los archivos de origen se encuentran en los directorios correspondientes al paquete o subpaquete en que están incluidos. Por lo tanto, el árbol de fuentes sigue la estructura del paquete. En C#, la ubicación de un archivo fuente no tiene nada que ver con su espacio de nombres.

Ninguna de las opciones tiene una superioridad significativa en el poder, solo se utilizan diferentes mecanismos para resolver las ambigüedades [18] .

Ubicación del texto fuente en los archivos

En C#, las clases se pueden colocar en archivos de forma arbitraria. El nombre del archivo de código fuente no tiene nada que ver con los nombres de las clases definidas en él. Se permite colocar varias clases públicas en un archivo. A partir de la versión 2.0, C# también le permite dividir una clase en dos o más archivos (palabra clave partial). La última característica está hecha para separar el código escrito por una persona y el código generado. Se utiliza, por ejemplo, en herramientas de construcción de interfaces visuales: la parte de la clase que contiene los campos y métodos controlados por el constructor de la interfaz se separa en un archivo separado.

En Java, cada archivo puede contener solo una clase pública, y Java requiere que el nombre del archivo sea el mismo que el nombre de la clase, lo que elimina la confusión de nombres de archivos y clases. Además, de acuerdo con la convención de formato de código recomendada por Sun, el tamaño de un archivo de código fuente no debe exceder las 2000 líneas de código , porque un archivo más grande es más difícil de analizar. Un tamaño de archivo grande también se considera un signo de mal diseño.

Excepciones

Ambos lenguajes admiten un mecanismo de manejo de excepciones que está diseñado sintácticamente exactamente de la misma manera: el lenguaje tiene un operador de lanzamiento de excepciones throwy un bloque de manejo de excepciones try{}catch(){}finally{}que proporciona la intercepción de las excepciones que han surgido dentro del bloque, así como su procesamiento. como la ejecución garantizada de las acciones finales.

Java admite excepciones comprobadas (checked) : el programador debe especificar explícitamente para cada método los tipos de excepciones que el método puede generar, estos tipos se enumeran en la declaración del método después de la palabra clave throws. Si un método usa métodos que lanzan excepciones verificadas, debe capturar explícitamente todas estas excepciones o incluirlas en su propia declaración. Por lo tanto, el código contiene explícitamente una lista de excepciones que cada método puede generar. La jerarquía de tipos de excepción también contiene dos tipos ( RuntimeExceptiony Error), cuyos descendientes no se verifican y no deben declararse. Están reservados para excepciones en tiempo de ejecución que pueden ocurrir en cualquier lugar, o que normalmente no pueden ser manejadas por el programador (como errores en tiempo de ejecución), y no deben declararse en un archivo throws.

C# no admite excepciones comprobadas. Su ausencia es una elección consciente de los desarrolladores. Anders Hejlsberg , arquitecto jefe de C#, cree que en Java fueron hasta cierto punto un experimento y no se justificaron [2] .

La utilidad de las excepciones comprobadas es discutible. Consulte el artículo Manejo de excepciones para obtener más información .

Programación paralela

En general, los mecanismos de programación paralela en C# son similares a los proporcionados por Java, la diferencia radica en los detalles de implementación. En ambos casos, hay una clase de biblioteca Thread que implementa el concepto de "thread". Java proporciona dos formas de crear subprocesos nativos, ya sea extendiendo la clase Thread o implementando la interfaz Runnable. En ambos casos, el programador debe definir un método run() heredado (incluido en la interfaz) que contiene el cuerpo del hilo, el código que se ejecutará en él. En cambio, C# usa el mecanismo de delegado: para crear un subproceso, se crea una instancia de la clase Thread estándar, a la que se pasa un delegado como parámetro del constructor, que contiene un método: el cuerpo del subproceso.

Ambos lenguajes tienen la capacidad de crear un bloque de código ejecutable sincrónicamente; en Java esto se hace con el operador sincronizado(), en C# con el operador lock(). También es posible en Java declarar métodos síncronos usando el modificador sincronizado en el encabezado de una declaración de método. Dichos métodos bloquean su objeto host cuando se ejecutan (por lo tanto, de los métodos de clase sincronizados, para la misma instancia, solo uno puede ejecutarse al mismo tiempo y solo en un hilo, el resto esperará). Se implementa una capacidad similar en .NET mediante el atributo de implementación del método MethodImplAttribute MethodImplOptions.Synchronized, pero a diferencia de Java, esta capacidad no forma parte formalmente del lenguaje C#.

C# tiene un operador lock(){} [3] que adquiere un bloqueo antes de ingresar a un bloque y lo libera tanto al salir como al lanzar una excepción. La contraparte de Java está sincronizada() {}.

C# 4.5 introdujo los operadores async y await [4] , así como una nueva clase Task que es más eficiente que Thread para tareas breves y simultáneas. El procesamiento paralelo eficiente de tipos enumerados (contenedores enumerables) se implementa en el mismo mecanismo. [5]

En ambos lenguajes también están disponibles idénticos medios de sincronización, basados ​​en enviar y esperar una señal de un hilo a otro (otros). En Java, estos son los métodos notificar(), notificarTodos() y esperar(), en C#, los métodos Pulse(), PulseAll(), Wait() (los tres métodos son funcionalmente similares en pares). La única diferencia es que en Java estos métodos (y, en consecuencia, la funcionalidad del monitor) se implementan en la clase Object, por lo que no se requieren bibliotecas adicionales para la sincronización, mientras que en C# estos métodos se implementan como métodos estáticos en una clase de biblioteca separada Monitor (implícitamente utilizado por el bloqueo del operador). En C#, la biblioteca estándar también contiene varias primitivas de sincronización adicionales para la ejecución paralela de subprocesos: mutexes, semáforos, temporizadores de sincronización. Desde la versión 1.5, JDK SE ha incluido los paquetes java.util.concurrent, java.util.concurrent.atomic y java.util.concurrent.locks, que contienen un conjunto integral de herramientas para implementar la computación paralela.

Código de bajo nivel

La interfaz nativa de Java (JNI) permite que los programas llamen a funciones específicas del sistema de bajo nivel (como las bibliotecas winAPI) desde Java. Como regla general, JNI se usa al escribir controladores. Al escribir bibliotecas JNI, un desarrollador debe usar una API especial que se proporciona de forma gratuita. También hay bibliotecas especializadas para la interacción de Java con COM.

La tecnología Platform Invoke (P/Invoke) implementada en .NET le permite llamar a código externo desde C#, que Microsoft denomina . A través de los atributos en los metadatos, el programador puede controlar con precisión el paso ( marshaling ) de parámetros y resultados, evitando así la necesidad de un código de personalización adicional. P/Invoke proporciona acceso casi completo a las API de procedimiento (como Win32 o POSIX ), pero no proporciona acceso directo a las bibliotecas de clases de C++.

.NET Framework también proporciona un puente entre .NET y COM , lo que le permite acceder a los componentes COM como si fueran objetos .NET nativos, lo que requiere un esfuerzo adicional del programador cuando se utilizan componentes COM con interfaces complejas no triviales (por ejemplo, en el caso de pasar estructuras a través de una matriz de bytes). En estos casos, debe recurrir a un código no seguro (consulte a continuación) u otras soluciones alternativas.

C# permite el uso limitado de punteros , que los diseñadores de lenguajes suelen encontrar peligrosos. El enfoque de C# para esto es requerir la palabra clave unsafeen bloques de código o métodos que usan esta función. Esta palabra clave advierte a los usuarios de dicho código de su peligro potencial. También requiere una opción de compilador explícita/insegura, que está desactivada de forma predeterminada. Dicho código "no seguro" se usa para mejorar la interacción con la API no administrada y, a veces, para mejorar la eficiencia de ciertas secciones del código.

C# también permite que el programador deshabilite la verificación de tipo normal y otras funciones de seguridad de CLR al permitir el uso de variables de puntero siempre que la extensión unsafe. La ventaja del código no seguro administrado sobre P/Invoke o JNI es que permite que el programador continúe trabajando en un entorno C# familiar para realizar tareas que, de otro modo, requerirían llamar a código no administrado escrito en otro idioma.

Implementaciones

JVM y CLR

Existen numerosas implementaciones de JVM para casi todas las plataformas del mercado. La JVM está siendo desarrollada por corporaciones como IBM , Sun Microsystems (desde 2010 Oracle ), Bea y varias otras. Cabe señalar que Sun (Oracle) lanza su JVM bajo su propia licencia [6] y bajo una licencia GPLv2 modificada (a través de la llamada "excepción Classpath") [7] Archivado el 3 de marzo de 2012. .

Java Web Start y los applets brindan un medio conveniente, liviano y seguro para distribuir aplicaciones de escritorio, y la eficiencia de su representación de código de bytes , junto con tecnologías de compresión agresivas como pack200 , hacen de Java una herramienta de distribución de aplicaciones web que requiere un uso intensivo del ancho de banda.

C# también es un estándar multiplataforma. Su plataforma principal es Windows , pero existen implementaciones para otras plataformas, la más importante de las cuales es el proyecto Mono .

.NET es una plataforma de desarrollo de código abierto universal mantenida por Microsoft y la comunidad .NET en GitHub. Es multiplataforma (compatible con Windows, macOS y Linux) y se puede utilizar para crear aplicaciones de dispositivos, nube e IoT.

ClickOnce ofrece una funcionalidad similar a Java Web Start, pero solo está disponible para clientes de Windows. Internet Explorer en Windows puede mostrar elementos de interfaz de .NET Windows Forms , lo que brinda una funcionalidad similar a la de un subprograma, pero está limitada a un navegador específico.

Estandarización

El desarrollo de estos dos lenguajes, así como sus API, formatos binarios y tiempos de ejecución, se gestionan de forma diferente.

C# está definido por los estándares ECMA e ISO , que definen la sintaxis del lenguaje, el formato del módulo ejecutable (conocido como CLI) y la biblioteca de clases base (BCL). Los estándares no incluyen muchas de las nuevas bibliotecas implementadas por Microsoft además del marco estándar, como bibliotecas para bases de datos, GUI y aplicaciones web ( Windows Forms , ASP.NET y ADO.NET ). Sin embargo, Microsoft acordó formalmente no demandar a los proyectos comunitarios por implementar estas bibliotecas [8]  (enlace inaccesible) .

Hasta la fecha, ningún componente del entorno Java ha sido estandarizado por Ecma , ISO , ANSI o cualquier otra organización de estándares de terceros. Si bien Oracle conserva los derechos legales exclusivos y sin restricciones para modificar y licenciar sus marcas registradas de Java, Oracle participa voluntariamente en un proceso llamado Java Community Process (JCP), que permite a las partes interesadas proponer cambios en cualquiera de las tecnologías Java de Oracle (lenguaje, kit de herramientas, API ) a través de consultas y grupos de expertos. Según las reglas del JCP, Oracle puede rechazar unilateralmente cualquier propuesta para cambiar el JDK , el entorno de tiempo de ejecución de Java o la especificación del lenguaje Java porque se requiere un voto de "sí" de Oracle para aprobarla. JCP requiere una cuota de membresía de los participantes comerciales, mientras que las organizaciones sin fines de lucro y los individuos pueden participar de forma gratuita.

Licencia

Si bien "Java" es una marca comercial de Oracle (anteriormente Sun), y solo Oracle puede otorgar licencias del nombre "Java", existen numerosos proyectos gratuitos que son parcialmente compatibles con Oracle Java. Por ejemplo, GNU Classpath y GNU Compiler for Java (GCJ) proporcionan una biblioteca de clases gratuita y un compilador parcialmente compatible con la versión actual de Oracle Java [19] . A finales de 2006, Sun anunció que todo el código fuente de Java, a excepción del código propietario sobre el que no conserva los derechos, se lanzaría como software libre en marzo de 2007 bajo una licencia GPL modificada [20] . Oracle actualmente distribuye su HotSpot Virtual Machine y el compilador de Java bajo la GPL, pero actualmente no existe una licencia gratuita para el tiempo de ejecución estándar de Java [21] [22] . Debido a que Oracle conservará la propiedad de su código fuente de Java, la publicación bajo la GPL no impedirá que Oracle distribuya versiones de Java que no sean gratuitas o de código abierto, ni que las licencie a otros [23] .

C#, CLR y la mayor parte de la biblioteca de clases relacionada están estandarizados y pueden implementarse libremente sin una licencia. Ya se han implementado varios sistemas C# gratuitos, incluidos Mono y DotGNU . El proyecto Mono también implementa muchas bibliotecas de Microsoft no estándar al aprender de los materiales de Microsoft, similar a GNU Classpath y Java. El objetivo del proyecto Mono es evitar infringir cualquier patente o derecho de autor, y el proyecto es de libre distribución y uso bajo la licencia GPL [24] . Microsoft está distribuyendo actualmente una versión de fuente compartida de su tiempo de ejecución .NET para uso no comercial [25] .

Uso

Ejecución de programas

Los intérpretes de Java se pueden instalar copiando archivos y funcionan sin restricciones en Windows desde al menos Windows 2000. El marco oficial de C# debe estar instalado en el sistema como administrador, ciertas versiones del lenguaje pueden requerir una determinada versión de Windows.

Java se basa en una cultura más abierta con empresas altamente competitivas en diferentes áreas de funcionalidad. La mayoría de las bibliotecas adicionales están disponibles bajo licencias gratuitas y de código abierto. Sun también fomenta la práctica de describir alguna funcionalidad como una especificación (consulte el proceso JCP), dejando la implementación a terceros (posiblemente proporcionando una implementación de referencia). Así, se resuelve el problema de la independencia del fabricante del software.

A pesar de la existencia de Mono , C# vincula estrechamente a los desarrolladores con la plataforma de Microsoft (incluido el sistema operativo y las soluciones de oficina). Por lo tanto, el usuario de software escrito en .NET a menudo no tiene opción para usar varios componentes del sistema. Esto conduce al llamado bloqueo de proveedores, en el que el fabricante del software de terceros puede dictar al comprador casi cualquier condición para respaldar el proyecto implementado. Mientras que el usuario de una aplicación Java, por regla general, puede elegir el proveedor de software adicional (como una base de datos, un sistema operativo, un servidor de aplicaciones, etc.).

Popularidad y desarrollo

Java es más antiguo que C# y está construido sobre una base de usuarios grande y activa, convirtiéndose en la lingua franca en muchas áreas modernas de la informática, especialmente aquellas que involucran redes . Java domina los cursos de programación en las universidades y colegios estadounidenses, y la literatura sobre Java hoy en día es mucho más grande que sobre C#. La madurez y popularidad de Java ha dado lugar a más bibliotecas y API en Java (muchas de las cuales son de código abierto) que en C#.

A diferencia de Java, C# es un lenguaje relativamente nuevo. Microsoft ha estudiado los lenguajes existentes como Java, Delphi y Visual Basic y ha cambiado algunos aspectos del lenguaje para adaptarse mejor a las necesidades de ciertos tipos de aplicaciones.

Con respecto a Java, se pueden escuchar críticas de que este lenguaje tiene un desarrollo lento, carece de algunas características que facilitan los patrones y metodologías de programación de moda. El lenguaje C# ha sido criticado por ser quizás demasiado rápido para adaptarse a las tendencias actuales en programación a costa del enfoque y la simplicidad del lenguaje. Aparentemente, los diseñadores de Java han adoptado una postura más conservadora al agregar nuevas funciones importantes a la sintaxis del lenguaje que en otros lenguajes modernos, tal vez porque no quieren vincular el lenguaje a corrientes que podrían conducir a callejones sin salida a largo plazo. Con el lanzamiento de Java 5.0, esta tendencia se revirtió en gran medida, ya que introdujo varias características nuevas importantes del lenguaje: foreachbucle de tipos, ajuste automático, métodos variados, tipos enumerados, tipos genéricos y anotaciones (todos los cuales también están presentes en C#). A partir de Java 8, comenzó la implementación activa de nuevas características, en particular: las expresiones lambda, la palabra clave var, la modularidad dentro del proyecto Jigsaw, etc.

C#, a su vez, evoluciona más rápido, con muchas menos restricciones para agregar nuevas funciones específicas de dominio. Esta tendencia fue especialmente evidente en la versión de C# 3.0, en la que, por ejemplo, aparecieron consultas tipo SQL . (Las nuevas funciones están diseñadas para seguir siendo un lenguaje de uso general. Para obtener más información sobre C# 3.0, consulte el artículo de C# ). Se han considerado adiciones específicas de dominio a Java pero, al menos hasta la fecha, se han abandonado.

Mercado

Desde la llegada de C#, se ha comparado constantemente con Java. Es innegable que C# y su CLR administrado le deben mucho a Java y su JRE (Java Runtime Environment).

Es discutible si el desarrollo de C# es de alguna manera el resultado del reconocimiento por parte de Microsoft de que un entorno de código administrado basado en Java tiene muchas ventajas en un mundo cada vez más interconectado, especialmente con la llegada de Internet en dispositivos distintos de las computadoras personales y la creciente importancia la seguridad de la red. Antes de la creación de C#, Microsoft modificó Java (creando J++ ) para agregar funciones que solo se ejecutan en Windows , violando así el acuerdo de licencia de Sun Microsystems . Mientras Microsoft estaba en la segunda fase de su estrategia comercial, conocida como " Abrazar, extender y extinguir ", el desarrollo de J++ se detuvo por una demanda presentada por Sun. Prohibido desarrollar un clon de Java con las características que quería, Microsoft creó una alternativa que estaba más en línea con sus necesidades y visión para el futuro.

A pesar de un comienzo tan agitado, cada vez es más claro que los dos idiomas rara vez compiten entre sí en el mercado. Java domina el sector móvil y tiene muchos seguidores en el mercado de aplicaciones web. C# ha sido bien recibido en el mercado de escritorio de Windows y gracias a ASP.NET, C# también es un jugador en el mercado de aplicaciones web.

Aplicaciones de escritorio

Para ambos idiomas, hay un conjunto de bibliotecas que brindan la capacidad de crear una interfaz de usuario para aplicaciones de escritorio. En el caso de Java, se trata de las bibliotecas multiplataforma Swing y SWT , así como de la plataforma JavaFX, que permite crear aplicaciones RIA. En principio, cualquiera de ellos te permite crear aplicaciones de escritorio multiplataforma en Java.

Para C# en la plataforma Windows, las principales plataformas para desarrollar aplicaciones gráficas de escritorio son las plataformas Windows Forms y WPF. Para el desarrollo bajo Windows 8 existe una plataforma especial WinRT . Para el desarrollo de Windows 10, hay una plataforma UWP dedicada. Para otras plataformas, se utiliza la biblioteca gtk#, creada por el proyecto Mono. Se han hecho y se están haciendo intentos de implementar Windows.Forms libremente (por ejemplo, en el proyecto DotGNU ), sin embargo, debido a la naturaleza cerrada del original, inevitablemente sufren de secundaria e incompletitud, difícilmente pueden competir con la implementación. de Microsoft y, por lo tanto, solo se puede utilizar para la transferencia retrasada de aplicaciones de Windows a otras plataformas. Los desarrollos que originalmente se basan en Windows generalmente se crean en Windows.Forms, y se vuelve difícil migrarlos a otra plataforma. El desarrollo de Mono C# usando gtk# es portable, pero mucho menos. No hay implementación del marco WPF dentro del proyecto Mono, por lo que las aplicaciones WPF no son portátiles para los sistemas operativos basados ​​en Linux.

C#, junto con Java, se está haciendo cada vez más popular en varios sistemas operativos basados ​​en Linux y BSD [26] [27] [28] . La implementación del proyecto Mono fue un proceso legalmente sencillo, ya que el lenguaje CLR y C# están estandarizados por Ecma e ISO, y cualquiera puede implementarlos sin preocuparse por el aspecto legal de las cosas [29] . Al mismo tiempo, debe tenerse en cuenta que una aplicación escrita en el entorno de Windows puede tener problemas de inicio significativos en otro sistema operativo.

Aplicaciones móviles

J2ME (JavaME, Java(2) Micro Edition) tiene una base muy amplia en los mercados de teléfonos móviles y PDA , donde solo los dispositivos más baratos carecen de KVM (una máquina virtual Java simplificada para dispositivos con recursos limitados). Los programas Java, incluidos muchos juegos, son omnipresentes.

Si bien casi todos los teléfonos incluyen KVM, la mayoría de los usuarios no utilizan mucho estas funciones. Las aplicaciones de Java en la mayoría de los teléfonos suelen consistir en sistemas de menús, pequeños juegos, etc. Las aplicaciones completas para teléfonos móviles son raras.

Java se utiliza para desarrollar aplicaciones para Android utilizando la máquina virtual Dalvik no estándar (o ART ).

C# es el lenguaje principal para escribir aplicaciones para el sistema operativo móvil Windows Phone desarrollado por Microsoft. Sin embargo, existe un marco de desarrollo multiplataforma de Xamarin que le permite crear aplicaciones nativas para Android, IOS y Windows Phone.

Véase también

Notas

  1. 1 2 Radeck, Kirk C# y Java: Comparación de  lenguajes de programación . MSDN (octubre de 2003). Consultado el 19 de noviembre de 2013. Archivado desde el original el 28 de noviembre de 2013.
  2. 1 2 3 Kurniawan, Budi Comparación de C# y  Java . O'Reilly Media (6 de julio de 2001). Consultado el 18 de noviembre de 2013. Archivado desde el original el 10 de junio de 2015.
  3. Chandra, Shyamal Suhana; Chandra, Kailash. Una comparación de Java y C#  // Journal of Computing Sciences in Colleges. - Consorcio para las Ciencias de la Computación en las Universidades, 2005. - Vol. 20 , n º 3 . - S. 238-254 . — ISSN 1937-4771 .
  4. 1 2 Gruntz, Dominik. C# y Java: las distinciones inteligentes  //  Journal of Object Technology. - 2002. - Edicion. noviembre-diciembre , n. vol. 1, no. 5 . - pág. 163-176 . Archivado desde el original el 18 de marzo de 2014.
  5. Lo nuevo en C# 7 y lo que ya es compatible con Visual Studio “15” Preview 4  (ruso) . Archivado desde el original el 21 de septiembre de 2017. Consultado el 21 de septiembre de 2017.
  6. BillWagner. Guía de programación C#. Constructores  (ruso)  ? . docs.microsoft.com . Consultado el 29 de octubre de 2021. Archivado desde el original el 29 de octubre de 2021.
  7. Rowe, 2004 , págs. 204-206.
  8. Johnson, Mark C# : ¿Una alternativa de lenguaje o simplemente J--?, Parte 2  . JavaWorld (21 de diciembre de 2000). Recuperado: 18 de noviembre de 2013.  (enlace inaccesible)
  9. Krikorian, Raffi Contrasting C# and Java  Syntax . O'Reilly Media (14 de junio de 2001). Consultado el 19 de noviembre de 2013. Archivado desde el original el 10 de junio de 2015.
  10. Aunque el propio tiempo de ejecución le permite generar e instanciar tipos derivados de los herederos de System.ValueType.
  11. sellado (Referencia de C#) . Consultado el 16 de marzo de 2010. Archivado desde el original el 5 de marzo de 2010.
  12. ↑ Genéricos en C# , Java y C++ . Archivado el 7 de octubre de 2006 en Wayback Machine . 
  13. Agosto de 1998 Java News . Consultado el 11 de junio de 2008. Archivado desde el original el 25 de enero de 2009.
  14. Cabrera, 2002 , págs. 30-32.
  15. Puvvala, 2003 , págs. 62-63.
  16. Balagurusamy, 2008 , pág. ocho.
  17. Jeffrey Richter CLR vía C# // M., Russian Edition Publishing House, 2007 - P.656. ISBN 978-5-91180-303-2
  18. 1 2 3 Johnson, Mark C# : ¿Una alternativa de lenguaje o simplemente J--?, Parte 1  . JavaWorld (22 de noviembre de 2000). Recuperado: 18 de noviembre de 2013.  (enlace inaccesible)
  19. ↑ Resultados de la comparación jdk15 vs. classpath Archivado el 28 de septiembre de 2007 en la actualidad . 
  20. Tecnologías relacionadas | Oracle (enlace descendente) . Consultado el 4 de diciembre de 2006. Archivado desde el original el 14 de mayo de 2007. 
  21. Sun openjdk: Inicio (enlace descendente) . Consultado el 4 de diciembre de 2006. Archivado desde el original el 11 de junio de 2007. 
  22. Acuerdo de licencia de Sun Java 2 Runtime . Consultado el 23 de noviembre de 2006. Archivado desde el original el 2 de enero de 2007.
  23. Licencia Pública General GNU - Proyecto GNU - Free Software Foundation (FSF) . Consultado el 4 de diciembre de 2006. Archivado desde el original el 5 de diciembre de 2006.
  24. Mono FAQ: Licencias (Patentes) . Consultado el 4 de diciembre de 2006. Archivado desde el original el 24 de junio de 2018.
  25. Rotor: Shared Source CLI proporciona código fuente para una implementación de FreeBSD de .NET (enlace no disponible) . Consultado el 4 de diciembre de 2006. Archivado desde el original el 2 de diciembre de 2006. 
  26. Fedora adopta Mono - ZDNet UK (enlace no disponible) . Consultado el 23 de noviembre de 2006. Archivado desde el original el 27 de diciembre de 2007. 
  27. Debian-mono . Consultado el 23 de noviembre de 2006. Archivado desde el original el 25 de diciembre de 2006.
  28. Wikipedia usa mono; Mono Integrado en Ubuntu/Debian - OSNews.com . Consultado el 23 de noviembre de 2006. Archivado desde el original el 7 de enero de 2006.
  29. Desarrollo de normas ISO:ISOTC home:00. Normas ISO y patentes . Consultado el 23 de noviembre de 2006. Archivado desde el original el 9 de diciembre de 2006.

Literatura

  • Balagurusamy, E. Programación en C#: Introducción. - Tata McGraw-Hill, 2008. - 540 p. — ISBN 0070667578 .
  • Cabrera, Haroldo et al. C# para programadores de Java. - Rockland: Syngress Publishing, 2002. - 613 p. — ISBN 1-931836-54-X .
  • Jones, Allen; Freeman, Adam. C# para desarrolladores de Java. - O'Reilly Media , 2010. - 576 p. - (Pro-desarrollador). - ISBN 978-0-7356-1779-1 .
  • Puvvala, Jawahar; Pota, Alok. .NET para desarrolladores de Java: Migración a C#. - Addison-Wesley Professional , 2003. - 720 p. - ISBN 978-0672324024 .
  • Rowe, Glenn W. De Java a C#. - Addison Wesley , 2004. - 688 p. — ISBN 978-0321155726 .

Enlaces