stringtranslate.com

Comparación entre C Sharp y Java

En este artículo se comparan dos lenguajes de programación : C# y Java . Aunque el objetivo principal de este artículo son los lenguajes y sus características, esta comparación también tendrá en cuenta algunas características de las plataformas y bibliotecas . Para una comparación más detallada de las plataformas, consulte Comparación de las plataformas Java y .NET .

C# y Java son lenguajes similares que se tipan de forma estática, fuerte y manifiesta . Ambos están orientados a objetos y diseñados con semiinterpretación o compilación en tiempo de ejecución , y ambos son lenguajes entre llaves , como C y C++ .

Tipos

Sistema de tipos unificado

Ambos lenguajes están tipados estáticamente con orientación a objetos basada en clases. En Java, los tipos primitivos son especiales en el sentido de que no están orientados a objetos y no podrían haber sido definidos utilizando el lenguaje mismo. Tampoco comparten un ancestro común con los tipos de referencia. Todos los tipos de referencia de Java derivan de un tipo raíz común. C# tiene un sistema de tipos unificado en el que todos los tipos (excepto los punteros no seguros [17] ) derivan en última instancia de un tipo raíz común. En consecuencia, todos los tipos implementan los métodos de este tipo raíz, y los métodos de extensión definidos para el tipo de objeto se aplican a todos los tipos, incluso a los literales int primitivos y a los delegados . Esto permite que C#, a diferencia de Java, admita objetos con encapsulación que no sean tipos de referencia.

En Java, los tipos compuestos son sinónimos de tipos de referencia; no se pueden definir métodos para un tipo a menos que también sea un tipo de referencia de clase . En C#, los conceptos de encapsulación y métodos se han desvinculado del requisito de referencia para que un tipo pueda admitir métodos y encapsulación sin ser un tipo de referencia. Sin embargo, solo los tipos de referencia admiten métodos virtuales y especialización.

Ambos lenguajes admiten muchos tipos integrados que se copian y pasan por valor en lugar de por referencia. Java denomina a estos tipos tipos primitivos , mientras que en C# se denominan tipos simples . Los tipos primitivos/simples suelen tener compatibilidad nativa con la arquitectura del procesador subyacente.

Los tipos simples de C# implementan varias interfaces y, en consecuencia, ofrecen muchos métodos directamente en las instancias de los tipos, incluso en los literales. Los nombres de los tipos de C# también son meros alias de los tipos de Common Language Runtime (CLR). El System.Int64tipo de C# es exactamente el mismo tipo que el tipo largo ; la única diferencia es que el primero es el nombre canónico de .NET , mientras que el segundo es un alias de C# para él.

Java no ofrece métodos directamente sobre tipos primitivos. En su lugar, los métodos que operan sobre valores primitivos se ofrecen a través de clases contenedoras primitivas complementarias . Existe un conjunto fijo de tales clases contenedoras, cada una de las cuales envuelve uno de los tipos primitivos del conjunto fijo. Como ejemplo, el tipo Java Long es un tipo de referencia que envuelve el tipo primitivo long . Sin embargo, no son el mismo tipo.

Tipos de datos

Tipos numéricos

Enteros con signo

Tanto Java como C# admiten números enteros con signo con anchos de bits de 8, 16, 32 y 64 bits. Utilizan el mismo nombre o alias para los tipos, excepto para el número entero de 8 bits que se denomina byte en Java y sbyte (byte con signo) en C#.

Enteros sin signo

C# admite tipos de enteros sin signo además de los tipos de enteros con signo . Los tipos sin signo son byte , ushort , uint y ulong para anchos de 8, 16, 32 y 64 bits, respectivamente. También se admiten operaciones aritméticas sin signo en los tipos. Por ejemplo, sumar dos enteros sin signo ( uint ) sigue dando como resultado un uint ; no un entero long o con signo.

Java no tiene tipos enteros sin signo. En particular, Java carece de un tipo primitivo para un byte sin signo . En cambio, el tipo byte de Java es sign extended , lo que es una fuente común de errores y confusión. [18]

Los enteros sin signo se excluyeron deliberadamente de Java porque James Gosling creía que los programadores no entenderían cómo funciona la aritmética sin signo.

En el diseño de lenguajes de programación, uno de los problemas habituales es que el lenguaje se vuelve tan complejo que nadie puede entenderlo. Uno de los pequeños experimentos que intenté fue preguntar a la gente sobre las reglas de la aritmética sin signo en C. Resulta que nadie entiende cómo funciona la aritmética sin signo en C. Hay algunas cosas obvias que la gente entiende, pero mucha gente no las entiende. [9] [19]

Las versiones 8 y 9 de Java agregaron algunas operaciones integradas limitadas con enteros sin signo, pero solo se exponen como métodos estáticos en las clases contenedoras primitivas; operan en tipos de enteros primitivos con signo, tratándolos como si no tuvieran signo. [20]

Números decimales de alta precisión

C# tiene una notación literal y de tipo para aritmética decimal de alta precisión (28 dígitos decimales) que es apropiada para cálculos financieros y monetarios. [21] [22] [23] A diferencia de los tipos de datos float y double , los números fraccionarios decimales como 0,1 se pueden representar exactamente en la representación decimal. En las representaciones float y double, dichos números a menudo tienen expansiones binarias no terminales, lo que hace que esas representaciones sean más propensas a errores de redondeo. [22]

Si bien Java no cuenta con un tipo incorporado, la biblioteca Java sí cuenta con un tipo decimal de precisión arbitraria . Este no se considera un tipo de lenguaje y no admite los operadores aritméticos habituales; en cambio, es un tipo de referencia que debe manipularse mediante los métodos de tipo. Vea más información sobre números de precisión y tamaño arbitrarios a continuación.

Tipos numéricos avanzados

Ambos lenguajes ofrecen tipos aritméticos de precisión arbitraria definidos por biblioteca para cálculos con números enteros y puntos decimales de tamaño arbitrario.

Solo Java tiene un tipo de datos para cálculos de punto decimal de precisión arbitraria. Solo C# tiene un tipo para trabajar con números complejos .

En ambos lenguajes, la cantidad de operaciones que se pueden realizar en los tipos numéricos avanzados es limitada en comparación con los tipos de punto flotante IEEE 754 integrados . Por ejemplo, ninguno de los tipos de tamaño arbitrario admite la raíz cuadrada o los logaritmos .

C# permite que los tipos definidos por bibliotecas se integren con tipos y operadores existentes mediante el uso de conversiones implícitas/explícitas personalizadas y sobrecarga de operadores. Consulte el ejemplo en la sección Integración de tipos definidos por bibliotecas

Personajes

Ambos lenguajes tienen un tipo de datos char (carácter) nativo como tipo simple. Aunque el tipo char se puede utilizar con operadores bit a bit, esto se realiza convirtiendo el valor char en un valor entero antes de la operación. Por lo tanto, el resultado de una operación bit a bit es un tipo numérico, no un carácter, en ambos lenguajes.

Tipos de datos compuestos integrados

Ambos lenguajes tratan las cadenas como objetos ( inmutables ) de tipo de referencia. En ambos lenguajes, el tipo contiene varios métodos para manipular cadenas, analizar, formatear, etc. En ambos lenguajes, las expresiones regulares se consideran una característica externa y se implementan en clases separadas.

Las bibliotecas de ambos lenguajes definen clases para trabajar con fechas, horas, zonas horarias y calendarios en diferentes culturas. Java proporciona java.util.Date, un tipo de referencia mutable con precisión de milisegundos, y (desde Java 8) el java.timepaquete (que incluye clases como LocalDate , LocalTime y LocalDateTime para valores de solo fecha, solo hora y fecha y hora), un conjunto de tipos de referencia inmutables con precisión de nanosegundos . [24] Por el contrario, C# es un tipo de valor de estructura inmutable para información de fecha y hora con precisión de 100 nanosegundos; la API de .NET 6 también agregó y , estructuras similares para operaciones de solo fecha o solo hora. [25] C# define además un tipo para trabajar con períodos de tiempo; Java 8 proporciona la clase para el mismo propósito. Ambos lenguajes admiten la aritmética de fecha y hora según diferentes culturas y zonas horarias.System.DateTimeSystem.DateOnlySystem.TimeOnlySystem.TimeSpanjava.time.Duration

Tipo de valor definido por el usuario (estructura)

C# permite al programador crear tipos de valor definidos por el usuario , utilizando la palabra clave struct . A diferencia de las clases y como los primitivos estándar, estos tipos de valor se pasan y se asignan por valor en lugar de por referencia. También pueden ser parte de un objeto (ya sea como un campo o en un cuadro ) o almacenarse en una matriz sin la indirección de memoria que normalmente existe para los tipos de clase.

Debido a que los tipos de valor no tienen noción de un valor nulo y se pueden usar en matrices sin inicialización, siempre vienen con un constructor predeterminado implícito que esencialmente llena el espacio de memoria de la estructura con ceros. El programador solo puede definir constructores adicionales con uno o más argumentos. Los tipos de valor no tienen tablas de métodos virtuales y, debido a eso (y a la huella de memoria fija ), están implícitamente sellados. Sin embargo, los tipos de valor pueden (y con frecuencia lo hacen) implementar interfaces . Por ejemplo, los tipos enteros integrados implementan varias interfaces .

Aparte de los tipos primitivos incorporados, Java no incluye el concepto de tipos de valor.

Enumeraciones

Ambos lenguajes definen enumeraciones, pero se implementan de maneras fundamentalmente diferentes. Por ello, las enumeraciones son un área en la que fallan las herramientas diseñadas para traducir automáticamente código entre los dos lenguajes (como los conversores de Java a C#).

C# ha implementado enumeraciones de una manera similar a C, es decir, como envoltorios alrededor de los indicadores de bits implementados en tipos integrales primitivos (int, byte, short, etc.). Esto tiene beneficios de rendimiento y mejora la interacción con el código compilado de C/C++, pero proporciona menos funciones y puede provocar errores si los tipos de valor de bajo nivel se convierten directamente a un tipo de enumeración, como se permite en el lenguaje C#. Por lo tanto, se considera un azúcar sintáctico . [26] Por el contrario, Java implementa enumeraciones como una colección de instancias con todas las funciones, que requiere más memoria y no ayuda a la interacción con el código C/C++, pero proporciona funciones adicionales en la reflexión y el comportamiento intrínseco. La implementación en cada lenguaje se describe en la siguiente tabla.

Tanto en C# como en Java, los programadores pueden usar enumeraciones en una sentencia switch sin conversión a una cadena o a un tipo entero primitivo. Sin embargo, C# no permite el paso implícito a menos que la sentencia case no contenga ningún código, ya que es una causa común de errores difíciles de encontrar. [29] El paso implícito debe declararse explícitamente mediante una sentencia goto . [30]

Delegados, referencias de métodos

C# implementa punteros de métodos orientados a objetos en forma de delegados . Un delegado es un tipo especial que puede capturar una referencia a un método. Esta referencia puede luego almacenarse en una variable de tipo delegado o pasarse a un método a través de un parámetro delegado para su posterior invocación. Los delegados de C# admiten covarianza y contravarianza , y pueden contener una referencia a cualquier método estático, método de instancia, método anónimo o expresión lambda compatible con la firma .

Los delegados no deben confundirse con los cierres y las funciones en línea. Los conceptos están relacionados porque una referencia a un cierre o función en línea debe estar capturada en una referencia de delegado para que sea útil. Pero un delegado no siempre hace referencia a una función en línea; también puede hacer referencia a métodos estáticos o de instancia existentes. Los delegados forman la base de los eventos de C#, pero tampoco deben confundirse con ellos.

Los delegados fueron deliberadamente excluidos de Java porque se los consideró innecesarios y perjudiciales para el lenguaje, y debido a posibles problemas de rendimiento. [31] En su lugar, se utilizan mecanismos alternativos. El patrón wrapper , que se asemeja a los delegados de C# en que permite al cliente acceder a uno o más métodos definidos por el cliente a través de una interfaz conocida , es uno de esos mecanismos. [ cita requerida ] Otro es el uso de objetos adaptadores que utilizan clases internas, que los diseñadores de Java argumentaron que son una mejor solución que las referencias de métodos enlazados. [31]

Consulte también ejemplos de delegados de C# y construcciones Java equivalentes.

Tipos elevados (que aceptan valores nulos)

C# permite que los tipos de valor/primitivos/simples se "eleven" para permitir el valor nulo especial además de los valores nativos del tipo. Un tipo se eleva agregando un sufijo al nombre del tipo; esto es equivalente a usar el tipo genérico , donde T es el tipo que se va a elevar. Las conversiones se definen implícitamente para convertir entre valores de la base y el tipo elevado. El tipo elevado se puede comparar con null o se puede probar para HasValue . Además, los operadores elevados se definen implícita y automáticamente en función de su base no elevada, donde, con la excepción de algunos operadores booleanos, un argumento nulo se propagará al resultado.?Nullable<T>

Java no admite la elevación de tipos como concepto, pero todos los tipos primitivos integrados tienen tipos envolventes correspondientes, que sí admiten el valor nulo en virtud de ser tipos de referencia (clases).

Según la especificación de Java, cualquier intento de desreferenciar la referencia nula debe dar como resultado una excepción lanzada en tiempo de ejecución, específicamente una NullPointerException . (No tendría sentido desreferenciarla de otra manera, porque, por definición, no apunta a ningún objeto en la memoria). Esto también se aplica cuando se intenta desempaquetar una variable de un tipo contenedor, que evalúa a null : el programa lanzará una excepción, porque no hay ningún objeto para desempaquetar y, por lo tanto, ningún valor encajonado para participar en el cálculo posterior.

El siguiente ejemplo ilustra el comportamiento diferente. En C#, el operador lifting* propaga el valor nulo del operando; en Java, al desempaquetar la referencia nula se genera una excepción.

No todos los operadores de C# que se han eliminado se han definido para propagar valores nulos de forma incondicional si uno de los operandos es nulo . En concreto, los operadores booleanos se han eliminado para admitir la lógica ternaria, manteniendo así la impedancia con SQL .

Los operadores booleanos de Java no admiten lógica ternaria ni están implementados en la biblioteca de clases base.

Tipo de enlace tardío (dinámico)

C# cuenta con un tipo dinámico de enlace tardío que admite la invocación dinámica sin reflexión, la interoperabilidad con lenguajes dinámicos y el enlace ad hoc a (por ejemplo) modelos de objetos de documentos. El tipo dinámico resuelve el acceso a los miembros de forma dinámica en tiempo de ejecución, en lugar de hacerlo de forma estática o virtual en tiempo de compilación. El mecanismo de búsqueda de miembros es extensible con la reflexión tradicional como mecanismo de respaldo.

Hay varios casos de uso para el tipo dinámico en C#:

Java no admite un tipo de enlace tardío. Los casos de uso para el tipo dinámico de C# tienen diferentes construcciones correspondientes en Java:

Véase también el ejemplo #Interoperabilidad con lenguajes dinámicos.

Punteros

Java excluye los punteros y la aritmética de punteros dentro del entorno de ejecución de Java. Los diseñadores del lenguaje Java razonaron que los punteros son una de las principales características que permiten a los programadores introducir errores en su código y decidieron no admitirlos. [9] Java no permite pasar y recibir directamente objetos/estructuras hacia/desde el sistema operativo subyacente y, por lo tanto, no necesita modelar objetos/estructuras para un diseño de memoria tan específico, diseños que con frecuencia implicarían punteros. La comunicación de Java con el sistema operativo subyacente se basa en cambio en la Interfaz nativa de Java (JNI), donde la comunicación con/adaptación a un sistema operativo subyacente se maneja a través de una capa de unión externa .

Aunque C# permite el uso de punteros y la aritmética de punteros correspondiente, los diseñadores del lenguaje C# tenían las mismas preocupaciones de que los punteros podrían usarse potencialmente para eludir las estrictas reglas de acceso a objetos. Por lo tanto, C# también excluye los punteros por defecto. [33] Sin embargo, debido a que los punteros son necesarios cuando se invocan muchas funciones nativas, se permiten punteros en un modo inseguro explícito . Los bloques de código o métodos que usan los punteros deben estar marcados con la palabra clave inseguro para poder usar punteros, y el compilador requiere el modificador para permitir la compilación de dicho código. Los ensamblajes que se compilan utilizando el modificador se marcan como tales y solo se pueden ejecutar si se confía explícitamente en ellos. Esto permite usar punteros y aritmética de punteros para pasar y recibir objetos directamente hacia/desde el sistema operativo u otras API nativas utilizando el diseño de memoria nativo para esos objetos, al mismo tiempo que se aísla dicho código potencialmente inseguro en ensamblajes específicamente confiables./unsafe/unsafe

Tipos de referencia

En ambos lenguajes las referencias son un concepto central. Todas las instancias de clases son por referencia .

Si bien no es directamente evidente en la sintaxis del lenguaje en sí , ambos lenguajes admiten el concepto de referencias débiles . Una instancia a la que solo se hace referencia mediante referencias débiles es elegible para la recolección de elementos no utilizados como si no hubiera ninguna referencia. En ambos lenguajes, esta característica se expone a través de las bibliotecas asociadas, aunque en realidad es una característica central del entorno de ejecución.

Además de las referencias débiles, Java tiene referencias suaves . Son muy similares a las referencias débiles, pero la máquina virtual Java (JVM) no desasignará los objetos referenciados de manera suave hasta que se necesite la memoria.

Matrices y colecciones

Las matrices y las colecciones son conceptos presentes en ambos lenguajes.

La sintaxis utilizada para declarar y acceder a matrices es idéntica, excepto que C# ha agregado sintaxis para declarar y manipular matrices multidimensionales.

En algunos casos, las matrices multidimensionales pueden aumentar el rendimiento debido a una mayor localidad (ya que hay una desreferencia de puntero en lugar de una para cada dimensión de la matriz, como es el caso de las matrices escalonadas). Sin embargo, dado que todo acceso a los elementos de la matriz en una matriz multidimensional requiere multiplicación/desplazamiento entre las dos o más dimensiones, esto es una ventaja solo en escenarios de acceso muy aleatorio.

Otra diferencia es que se puede asignar toda la matriz multidimensional con una sola aplicación del operador new , mientras que las matrices irregulares requieren bucles y asignaciones para cada dimensión. Sin embargo, Java proporciona una construcción sintáctica para asignar una matriz irregular con longitudes regulares; los bucles y las asignaciones múltiples las realiza la máquina virtual y no necesitan ser explícitos en el nivel de origen.

Ambos lenguajes cuentan con un amplio conjunto de tipos de colecciones que incluye varios tipos de listas ordenadas y desordenadas, mapas/diccionarios, conjuntos, etc.

Tuplas

C# ofrece dos métodos diferentes para crear tipos de tuplas (también conocidos como tipos de producto ). El primero es a través de las System.Tupleclases, que son tipos de referencia inmutables proporcionados por la API del marco (a partir de .NET Framework 4.0) para crear tipos de tuplas genéricos. [14] Este primer método ha sido reemplazado desde entonces por el segundo, las System.ValueTupleestructuras, que son tipos de valores mutables proporcionados por la API del marco (a partir de .NET Framework 4.7). [15]

Aunque los dos métodos parecen superficialmente similares, tienen múltiples diferencias notables. Los tipos ValueTuple son tipos de valor, por lo que tienen una huella de memoria más compacta; además, los tipos ValueTuple exponen sus contenidos como campos mutables, en comparación con las propiedades inmutables de las clases Tuple . Finalmente, desde la versión 7.0 de C#, el lenguaje tiene soporte sintáctico nativo para la construcción, deconstrucción y manipulación de tuplas como instancias ValueTuple ; esto también permite el cambio de nombre arbitrario de los campos constituyentes de las tuplas (a diferencia de Tuple , donde los campos siempre se denominan Item1 , Item2 , etc.).

Java no proporciona tipos de tuplas como parte de su lenguaje o API estándar; existen numerosas bibliotecas de terceros que pueden proporcionar tipos de tuplas, [41] pero todas son necesariamente similares a las System.Tupleclases de C#. En comparación con los tipos ValueTuple de C# y su sintaxis asociada, son más difíciles de usar (requieren el uso explícito de constructores o métodos de fábrica estáticos para crearlos, requieren acceso de miembros individuales para deconstruirlos y tienen nombres fijos para sus elementos).

Expresiones y operadores

Empaquetado y desempaquetado

Ambos lenguajes permiten el boxing y unboxing automático , es decir, permiten la conversión implícita entre cualquier tipo primitivo y los tipos de referencia correspondientes.

En C#, los tipos primitivos son subtipos del tipo Object. En Java esto no es así; cualquier tipo primitivo dado y el tipo contenedor correspondiente no tienen una relación específica entre sí, excepto por el autoboxing y el unboxing, que actúan como azúcar sintáctico para intercambiar entre ellos. Esto se hizo intencionalmente, para mantener la compatibilidad con versiones anteriores de Java, en las que no se permitía la conversión automática y el programador trabajaba con dos conjuntos separados de tipos: los tipos primitivos y la jerarquía de tipos contenedor (de referencia). [46]

Esta diferencia tiene las siguientes consecuencias. En primer lugar, en C#, los tipos primitivos pueden definir métodos, como una anulación del ToString()método de Object. En Java, esta tarea la llevan a cabo las clases contenedoras primitivas .

En segundo lugar, en Java se necesita una conversión adicional cada vez que se intenta desreferenciar directamente un valor primitivo, ya que no se enmarcará automáticamente. La expresión convertirá un literal entero en una cadena en Java mientras que realiza la misma operación en C#. Esto se debe a que la última es una llamada de instancia en el valor primitivo 42 , mientras que la primera es una llamada de instancia en un objeto de tipo .((Integer)42).toString()42.ToString()java.lang.Integer

Finalmente, otra diferencia es que Java hace un uso intensivo de tipos en caja en genéricos (ver más abajo).

Declaraciones

Sintaxis

Ambos lenguajes se consideran lenguajes de "llaves" en la familia C/C++. En general, las sintaxis de los lenguajes son muy similares. La sintaxis a nivel de declaración y expresión es casi idéntica con una inspiración obvia de la tradición C/C++. A nivel de definición de tipos (clases e interfaces ) existen algunas diferencias menores. Java es explícito en cuanto a la extensión de clases e implementación de interfaces , mientras que C# lo infiere a partir del tipo de tipos del que deriva una nueva clase/ interfaz .

C# admite más funciones que Java, lo que hasta cierto punto también es evidente en la sintaxis que especifica más palabras clave y más reglas gramaticales que Java.

Palabras clave y compatibilidad con versiones anteriores

A medida que los lenguajes evolucionaron, los diseñadores de ambos lenguajes se enfrentaron a situaciones en las que querían ampliar los lenguajes con nuevas palabras clave o sintaxis. Las nuevas palabras clave, en particular, pueden romper el código existente en el nivel de fuente, es decir, el código antiguo puede dejar de compilarse si se presenta a un compilador para una versión posterior del lenguaje. Los diseñadores de lenguajes están interesados ​​en evitar tales regresiones. Los diseñadores de los dos lenguajes han seguido caminos diferentes al abordar este problema.

Los diseñadores del lenguaje Java han evitado las palabras clave nuevas tanto como han podido, prefiriendo en su lugar introducir nuevas construcciones sintácticas que antes no eran legales o reutilizar palabras clave existentes en nuevos contextos. De esta manera no ponían en peligro la compatibilidad con versiones anteriores. Un ejemplo de lo primero se puede encontrar en cómo se extendió el bucle for para aceptar tipos iterables. Un ejemplo de lo segundo se puede encontrar en cómo se reutilizaron las palabras clave extends y (especialmente) super para especificar límites de tipo cuando se introdujeron los genéricos en Java 1.5. En un momento (Java 1.4) se introdujo una nueva palabra clave assert que antes no estaba reservada como palabra clave. Esto tenía el potencial de invalidar código que antes era válido, si, por ejemplo, el código utilizaba assert como identificador. Los diseñadores decidieron abordar este problema con una solución de cuatro pasos: 1) Introducir un cambio de compilador que indique si se debe utilizar Java 1.4 o posterior, 2) Marcar assert como palabra clave solo al compilar como Java 1.4 y posterior, 3) Establecer como predeterminado 1.3 para evitar que el código anterior (que no es compatible con 1.4) no sea válido y 4) Emitir advertencias, si la palabra clave se utiliza en modo Java 1.3, para permitir cambios en el código.

Los diseñadores del lenguaje C# han introducido varias palabras clave nuevas desde la primera versión. Sin embargo, en lugar de definir estas palabras clave como palabras clave globales , las definen como palabras clave sensibles al contexto . Esto significa que incluso cuando introdujeron (entre otras) las palabras clave partial y yield en C# 2.0, el uso de esas palabras como identificadores sigue siendo válido ya que no hay conflicto posible entre el uso como palabra clave y el uso como identificador, dado el contexto. Por lo tanto, la sintaxis actual de C# es totalmente compatible con el código fuente escrito para cualquier versión anterior sin especificar la versión del lenguaje que se utilizará.

Programación orientada a objetos

Tanto C# como Java están diseñados desde cero como lenguajes orientados a objetos que utilizan un envío dinámico , con una sintaxis similar a C++ (C++ a su vez deriva de C ). Sin embargo, ninguno de los dos lenguajes es un superconjunto de C o C++.

Clase parcial

C# permite dividir una definición de clase en varios archivos fuente mediante una característica llamada clases parciales . Cada parte debe estar marcada con la palabra clave partial . Todas las partes deben presentarse al compilador como parte de una única compilación. Las partes pueden hacer referencia a miembros de otras partes. Las partes pueden implementar interfaces y una parte puede definir una clase base. La característica es útil en escenarios de generación de código (como el diseño de interfaz de usuario (UI)), donde un generador de código puede proporcionar una parte y el desarrollador otra parte para compilarlas juntas. De este modo, el desarrollador puede editar su parte sin el riesgo de que un generador de código sobrescriba ese código en algún momento posterior. A diferencia del mecanismo de extensión de clase, una clase parcial permite dependencias circulares entre sus partes, ya que se garantiza que se resolverán en tiempo de compilación. Java no tiene un concepto correspondiente.

Clases internas y locales

Ambos lenguajes permiten clases internas , en las que una clase se define léxicamente dentro de otra clase. Sin embargo, en cada lenguaje estas clases internas tienen una semántica bastante diferente.

En Java, a menos que la clase interna se declare como estática , una referencia a una instancia de una clase interna lleva consigo una referencia a la clase externa. Como resultado, el código en la clase interna tiene acceso tanto a los miembros estáticos como a los no estáticos de la clase externa. Para crear una instancia de una clase interna no estática, se debe nombrar la instancia de la clase externa que la abarca. [53] Esto se hace a través de un nuevo operador new introducido en JDK 1.3: . Esto se puede hacer en cualquier clase que tenga una referencia a una instancia de la clase externa.outerClassInstance.new Outer.InnerClass()

En C#, una clase interna es conceptualmente lo mismo que una clase normal. En cierto sentido, la clase externa solo actúa como un espacio de nombres. Por lo tanto, el código de la clase interna no puede acceder a miembros no estáticos de la clase externa a menos que lo haga a través de una referencia explícita a una instancia de la clase externa. Los programadores pueden declarar la clase interna como privada para permitir que solo la clase externa tenga acceso a ella.

Java proporciona otra característica llamada clases locales o clases anónimas , que se pueden definir dentro del cuerpo de un método. Por lo general, se utilizan para implementar una interfaz con solo uno o dos métodos, que suelen ser controladores de eventos. Sin embargo, también se pueden utilizar para anular los métodos virtuales de una superclase. Los métodos de esas clases locales tienen acceso a las variables locales del método externo declaradas como final . C# satisface los casos de uso para estas proporcionando delegados anónimos ; consulte el manejo de eventos para obtener más información sobre esto.

C# también proporciona una característica llamada tipos/clases anónimos , pero es bastante diferente del concepto de Java con el mismo nombre. Permite al programador crear una instancia de una clase proporcionando solo un conjunto de nombres para las propiedades que debe tener la clase y una expresión para inicializar cada una. Los tipos de las propiedades se infieren de los tipos de esas expresiones. Estas clases declaradas implícitamente se derivan directamente de object .

Evento

Los delegados de multidifusión de C# se utilizan con eventos . Los eventos brindan soporte para la programación basada en eventos y son una implementación del patrón de observador . Para respaldar esto, existe una sintaxis específica para definir eventos en clases y operadores para registrar, anular el registro o combinar controladores de eventos.

Consulte aquí para obtener información sobre cómo se implementan los eventos en Java.

Sobrecarga de operadores y conversiones

La sobrecarga de operadores y las conversiones definidas por el usuario son características independientes que tienen como objetivo permitir que los nuevos tipos se conviertan en ciudadanos de primera clase en el sistema de tipos. Al utilizar estas características en C#, se han integrado tipos como Complex y decimal para que los operadores habituales como la suma y la multiplicación funcionen con los nuevos tipos. A diferencia de C++, C# restringe el uso de la sobrecarga de operadores, prohibiéndola para los operadores new , , , , , y cualquier variación de declaraciones compuestas como . Pero los operadores compuestos llamarán a operadores simples sobrecargados, como llamar a y . [54]( )||&&=+=-=-=

Java no incluye sobrecarga de operadores ni conversiones personalizadas para evitar el abuso de la función y mantener el lenguaje simple. [55]

Indexador

C# también incluye indexadores que pueden considerarse un caso especial de sobrecarga de operadores (como C++ operator[]) o propiedades get / set parametrizadas . Un indexador es una propiedad que utiliza uno o más parámetros (índices); los índices pueden ser objetos de cualquier tipo:this[]

myList [ 4 ] = 5 ; cadena nombre = xmlNode . Atributos [ "nombre" ]; pedidos = customerMap [ elCliente ];       

Java no incluye indexadores. El patrón común de Java implica escribir métodos de obtención y establecimiento explícitos, mientras que un programador de C# utilizaría un indexador.

Campos e inicialización

Inicialización de objetos

Tanto en C# como en Java, los campos de un objeto se pueden inicializar mediante inicializadores de variables (expresiones que se pueden asignar a las variables donde están definidas) o mediante constructores (subrutinas especiales que se ejecutan cuando se crea un objeto). Además, Java contiene inicializadores de instancia , que son bloques de código anónimos sin argumentos que se ejecutan después de la llamada explícita (o implícita) al constructor de una superclase, pero antes de que se ejecute el constructor.

C# inicializa los campos de objeto en el siguiente orden al crear un objeto:

  1. Campos estáticos derivados
  2. Constructor estático derivado
  3. Campos de instancia derivados
  4. Campos estáticos básicos
  5. Constructor estático base
  6. Campos de instancia base
  7. Constructor de instancia base
  8. Constructor de instancia derivada

Algunos de los campos anteriores pueden no ser aplicables (por ejemplo, si un objeto no tiene campos estáticos ). Los campos derivados son aquellos que están definidos en la clase directa del objeto, mientras que el campo base es un término para los campos que están definidos en una de las superclases del objeto. Tenga en cuenta que una representación de objeto en la memoria contiene todos los campos definidos en su clase o cualquiera de sus superclases, incluso si algunos campos en las superclases están definidos como privados.

Se garantiza que cualquier inicializador de campo surta efecto antes de que se llame a cualquier constructor, ya que tanto el constructor de instancia de la clase del objeto como sus superclases se llaman después de que se llame a los inicializadores de campo. Sin embargo, existe una trampa potencial en la inicialización de objetos cuando se llama a un método virtual desde un constructor base. El método anulado en una subclase puede hacer referencia a un campo que está definido en la subclase, pero es posible que este campo no se haya inicializado porque el constructor de la subclase que contiene la inicialización de campo se llama después del constructor de su clase base.

En Java, el orden de inicialización es el siguiente:

  1. Invocación de otro constructor (ya sea de la clase del objeto o de la superclase del objeto)
  2. Inicializadores de variables de instancia e inicializadores de instancia (en el orden en que aparecen en el código fuente)
  3. El cuerpo del constructor

Al igual que en C#, un nuevo objeto se crea llamando a un constructor específico. Dentro de un constructor, la primera instrucción puede ser una invocación de otro constructor. Si esto se omite, el compilador agrega implícitamente la llamada al constructor sin argumentos de la superclase. De lo contrario, se puede llamar explícitamente a otro constructor sobrecargado de la clase del objeto o a un constructor de la superclase. En el primer caso, el constructor llamado volverá a llamar a otro constructor (ya sea de la clase del objeto o de su subclase) y la cadena, tarde o temprano, termina en la llamada a uno de los constructores de la superclase.

Después de llamar a otro constructor (que provoca la invocación directa del constructor de la superclase, y así sucesivamente, hasta llegar a la clase Object), se inicializan las variables de instancia definidas en la clase del objeto. Incluso si no hay inicializadores de variables definidos explícitamente para algunas variables, estas variables se inicializan con valores predeterminados. Tenga en cuenta que las variables de instancia definidas en las superclases ya están inicializadas en este punto, porque fueron inicializadas por un constructor de la superclase cuando se lo llamó (ya sea por el código del constructor o por inicializadores de variables realizados antes del código del constructor o implícitamente con valores predeterminados). En Java, los inicializadores de variables se ejecutan de acuerdo con su orden textual en el archivo fuente.

Finalmente, se ejecuta el cuerpo del constructor. Esto garantiza el orden correcto de inicialización, es decir, los campos de una clase base terminan la inicialización antes de que comience la inicialización de los campos de una clase de objeto.

Existen dos posibles trampas principales en la inicialización de objetos de Java. En primer lugar, los inicializadores de variables son expresiones que pueden contener llamadas a métodos. Dado que los métodos pueden hacer referencia a cualquier variable definida en la clase, el método llamado en un inicializador de variable puede hacer referencia a una variable que esté definida debajo de la variable que se está inicializando. Dado que el orden de inicialización corresponde al orden textual de las definiciones de variables, dicha variable no se inicializaría con el valor prescrito por su inicializador y contendría el valor predeterminado. Otra trampa potencial es cuando un método que se reemplaza en la clase derivada se llama en el constructor de la clase base, lo que puede llevar a un comportamiento que el programador no esperaría cuando se crea un objeto de la clase derivada. De acuerdo con el orden de inicialización, el cuerpo del constructor de la clase base se ejecuta antes de que se evalúen los inicializadores de variables y antes de que se ejecute el cuerpo del constructor de la clase derivada. Sin embargo, el método reemplazado llamado desde el constructor de la clase base puede hacer referencia a variables definidas en la clase derivada, pero estas aún no se inicializan con los valores especificados por sus inicializadores o establecidos en el constructor de la clase derivada. El último problema también se aplica a C#, pero de una forma menos crítica, ya que en C# los métodos no se pueden anular de forma predeterminada.

Eliminación de recursos

Ambos lenguajes utilizan principalmente la recolección de basura como un medio para recuperar recursos de memoria, en lugar de una desasignación explícita de memoria. En ambos casos, si un objeto contiene recursos de diferentes tipos además de la memoria, como identificadores de archivos, recursos gráficos, etc., entonces se le debe notificar explícitamente cuando la aplicación ya no lo usa. Tanto C# como Java ofrecen interfaces para dicha eliminación determinista y tanto C# como Java (desde Java 7) cuentan con instrucciones de administración automática de recursos que invocarán automáticamente los métodos de eliminación/cierre en esas interfaces.

Métodos

Métodos de extensión y métodos predeterminados

Al utilizar un designador this especial en el primer parámetro de un método, C# permite que el método actúe como si fuera un método miembro del tipo del primer parámetro. Esta extensión de la clase externa es puramente sintáctica. El método de extensión debe declararse estático y definirse dentro de una clase puramente estática. El método debe obedecer cualquier restricción de acceso a miembros como cualquier otro método externo a la clase; por lo tanto, los métodos estáticos no pueden romper la encapsulación de objetos. [61] [62] La "extensión" solo está activa dentro de los ámbitos donde se ha importado el espacio de nombres de la clase host estática.

Desde Java 8, Java tiene una característica similar llamada métodos predeterminados , que son métodos con un cuerpo declarado en interfaces. A diferencia de los métodos de extensión de C#, los métodos predeterminados de Java son métodos de instancia en la interfaz que los declara. La definición de métodos predeterminados en clases que implementan la interfaz es opcional: si la clase no define el método, se utiliza la definición predeterminada en su lugar.

Tanto los métodos de extensión de C# como los métodos predeterminados de Java permiten que una clase anule la implementación predeterminada del método de extensión/predeterminado, respectivamente. En ambos lenguajes, esta anulación se logra definiendo un método en la clase que debería utilizar una implementación alternativa del método.

Las reglas de alcance de C# definen que si se encuentra un método coincidente en una clase, este tiene prioridad sobre un método de extensión coincidente. En Java, se supone que cualquier clase declarada para implementar una interfaz con un método predeterminado tiene las implementaciones de los métodos predeterminados, a menos que la clase implemente el método en sí.

Métodos parciales

En relación con las clases parciales, C# permite especificar métodos parciales dentro de clases parciales. Un método parcial es una declaración intencional de un método con varias restricciones en la firma. Las restricciones garantizan que si ninguna parte de la clase proporciona una definición, entonces el método y cada llamada a este se pueden borrar de forma segura. [63] Esta característica permite que el código proporcione una gran cantidad de puntos de intercepción (como el patrón de diseño del método de plantilla GoF ) sin pagar ninguna sobrecarga de tiempo de ejecución si otra parte de la clase no está utilizando estos puntos de extensión en el momento de la compilación. Java no tiene un concepto correspondiente.

Métodos virtuales

Los métodos en C# no son virtuales de manera predeterminada y, si se desea, se deben declarar como virtuales explícitamente. En Java, todos los métodos no privados y no estáticos son virtuales. La virtualidad garantiza que siempre se llamará a la anulación más reciente del método, pero implica un cierto costo de tiempo de ejecución en la invocación, ya que estas invocaciones normalmente no se pueden incluir en línea y requieren una llamada indirecta a través de la tabla de métodos virtuales . Sin embargo, algunas implementaciones de JVM, incluida la implementación de referencia de Oracle, implementan la inclusión en línea de los métodos virtuales llamados con más frecuencia.

Los métodos de Java son virtuales de manera predeterminada (aunque se pueden sellar utilizando el modificador final para impedir su anulación). No hay forma de permitir que las clases derivadas definan un método nuevo y no relacionado con el mismo nombre.

Esto significa que, de manera predeterminada en Java, y solo cuando se habilita explícitamente en C#, se pueden definir nuevos métodos en una clase derivada con el mismo nombre y firma que los de su clase base. Cuando se llama al método en una referencia de superclase de dicho objeto, se llamará a la implementación anulada "más profunda" del método de la clase base de acuerdo con la subclase específica del objeto al que se hace referencia.

En algunos casos, cuando una subclase introduce un método con el mismo nombre y firma que un método ya presente en la clase base , pueden surgir problemas. En Java, esto significa que el método de la clase derivada anulará implícitamente el método de la clase base, aunque esa no sea la intención de los diseñadores de ninguna de las clases.

Para mitigar esto, C# requiere que si un método tiene la intención de anular un método heredado, se debe especificar la palabra clave override . De lo contrario, el método "ocultará" el método heredado. Si la palabra clave no está presente, se emite una advertencia del compilador a tal efecto, que se puede silenciar especificando la palabra clave new . Esto evita el problema que puede surgir de una clase base que se extiende con un método no privado (es decir, una parte heredada del espacio de nombres) cuya firma ya está en uso por una clase derivada. Java tiene una comprobación de compilador similar en forma de anotación de método, pero no es obligatoria y, en su ausencia, la mayoría de los compiladores no proporcionarán comentarios (pero el método se anulará).@Override

Parámetros constantes/inmutables

En Java, es posible evitar la reasignación de una variable local o un parámetro de método mediante el uso de la finalpalabra clave. La aplicación de esta palabra clave a una variable de tipo primitivo hace que la variable se vuelva inmutable. Sin embargo, la aplicación finala una variable de tipo de referencia solo evita que se le asigne otro objeto. No evitará que se muten los datos contenidos en el objeto. A partir de C#7, es posible evitar la reasignación de un parámetro de método mediante el uso de la palabra clave in , sin embargo, esta palabra clave no se puede utilizar en variables locales. Al igual que con Java, la aplicación de in a un parámetro solo evita que el parámetro se reasigne a un valor diferente. Todavía es posible mutar los datos contenidos en el objeto. [64]

Ambos lenguajes no admiten la característica esencial de corrección constante que existe en C / C++ , que hace que un método sea constante.

Java define la palabra "constante" arbitrariamente como un campo. Como convención, estos nombres de variable se escriben solo en mayúsculas y las palabras se separan con un guión bajo, pero el lenguaje Java no insiste en esto. Un parámetro que solo tiene no se considera una constante, aunque puede serlo en el caso de un tipo de datos primitivo o una clase inmutable , como una .static finalfinalString

Métodos generadores

Cualquier método de C# declarado como que devuelve IEnumerable , IEnumerator o las versiones genéricas de estas interfaces se puede implementar utilizando la sintaxis yield . Esta es una forma de continuaciones limitadas generadas por el compilador y puede reducir drásticamente el código necesario para recorrer o generar secuencias, aunque ese código simplemente lo genera el compilador. La característica también se puede utilizar para implementar secuencias infinitas, por ejemplo, la secuencia de números de Fibonacci .

Java no tiene una característica equivalente. En cambio, los generadores se definen normalmente proporcionando una implementación especializada de una colección conocida o una interfaz iterable, que calculará cada elemento a pedido. Para que un generador de este tipo se utilice en una declaración for each , debe implementar interface java.lang.Iterable.

Véase también el ejemplo de secuencia de Fibonacci a continuación.

Implementación de interfaz explícita

C# también tiene una implementación de interfaz explícita que permite a una clase implementar específicamente métodos de una interfaz , separarlos de sus propios métodos de clase o proporcionar diferentes implementaciones para dos métodos con el mismo nombre y firma heredados de dos interfaces base.

En cualquiera de los lenguajes, si se especifica un método (o propiedad en C#) con el mismo nombre y firma en varias interfaces, los miembros entrarán en conflicto cuando se diseñe una clase que implemente esas interfaces. Una implementación implementará por defecto un método común para todas las interfaces. Si se necesitan implementaciones separadas (porque los métodos sirven para propósitos diferentes o porque los valores de retorno difieren entre las interfaces), la implementación explícita de la interfaz de C# resolverá el problema, aunque permitirá diferentes resultados para el mismo método, dependiendo de la conversión actual del objeto. En Java no hay otra forma de resolver este problema que refactorizando una o más de las interfaces para evitar conflictos de nombres. [58]

Parámetros de referencia (entrada/salida)

En Java, los argumentos de los tipos primitivos (por ejemplo, int, double) a un método se pasan por valor, mientras que los objetos se pasan por referencia. Esto significa que un método opera sobre copias de los tipos primitivos que se le pasan en lugar de sobre las variables reales. Por el contrario, en algunos casos los objetos reales pueden cambiar. En el siguiente ejemplo, el objeto String no se cambia. Se cambia el objeto de la clase 'a'.

En C#, es posible hacer cumplir una referencia con la palabra clave ref , de forma similar a C++ y en cierto sentido a C. Esta característica de C# es particularmente útil cuando se desea crear un método que devuelva más de un objeto. En Java, no se puede intentar devolver varios valores de un método a menos que se utilice un contenedor, en este caso llamado "Ref". [65]

Excepciones

Excepciones comprobadas

Java admite excepciones controladas (junto con excepciones no controladas). C# solo admite excepciones no controladas. Las excepciones controladas obligan al programador a declarar la excepción lanzada en un método o a capturar la excepción lanzada mediante una cláusula try-catch .

Las excepciones comprobadas pueden fomentar una buena práctica de programación, garantizando que se solucionen todos los errores. Sin embargo, Anders Hejlsberg , arquitecto jefe del lenguaje C#, sostiene que, hasta cierto punto, fueron un experimento en Java y que no se ha demostrado que valgan la pena, excepto en pequeños programas de ejemplo. [67] [68]

Una crítica es que las excepciones comprobadas alientan a los programadores a usar un bloque catch vacío ( ), [69] que silenciosamente se traga las excepciones, en lugar de dejar que las excepciones se propaguen a una rutina de manejo de excepciones de nivel superior. En algunos casos, sin embargo, se puede aplicar en su lugar el encadenamiento de excepciones , volviendo a lanzar la excepción en una excepción contenedora. Por ejemplo, si se cambia un objeto para acceder a una base de datos en lugar de a un archivo, se podría capturar un y volver a lanzarlo como un , ya que el llamador puede no necesitar conocer el funcionamiento interno del objeto.catch (Exception e) {}SQLExceptionIOException

Sin embargo, no todos los programadores están de acuerdo con esta postura. James Gosling y otros sostienen que las excepciones controladas son útiles y que su mal uso ha causado los problemas. Es posible capturar excepciones de forma silenciosa, sí, pero se debe indicar explícitamente qué hacer con ellas, a diferencia de las excepciones no controladas que permiten no hacer nada por defecto. Se pueden ignorar, pero se debe escribir código explícitamente para ignorarlas. [70] [71]

Intentar-atrapar-finalmente

También existen diferencias entre los dos lenguajes en el tratamiento de la try-finallysentencia. El bloque finally siempre se ejecuta, incluso si el bloque try contiene sentencias de paso de control como throw o return . En Java, esto puede dar lugar a un comportamiento inesperado si el bloque try es abandonado por una sentencia return con algún valor y, a continuación, el bloque finally que se ejecuta a continuación también es abandonado por una sentencia return con un valor diferente. C# resuelve este problema prohibiendo cualquier sentencia de paso de control como return o break en el bloque finally .

Una razón común para usar try-finallybloques es proteger el código de administración de recursos, garantizando así la liberación de recursos valiosos en el bloque finally. C# presenta la declaración using como una abreviatura sintáctica para este escenario común, en el que siempre se llama al método del objeto de la instrucción using .Dispose()

Una diferencia bastante sutil es el momento en que se crea un seguimiento de la pila cuando se lanza una excepción. En Java, el seguimiento de la pila se crea en el momento en que se crea la excepción.

clase  Foo { Excepción arriba = nueva Excepción (); int foo () lanza Excepción { lanzar arriba ; } }              

La excepción en la declaración anterior siempre contendrá el seguimiento de la pila del constructor, sin importar con qué frecuencia se llame a foo. En C#, por otro lado, el seguimiento de la pila se crea en el momento en que se ejecuta "throw".

clase Foo { Excepción e = nueva Excepción ();       int foo () { try { throw e ; } catch ( Excepción e ) { throw ; } } }              

En el código anterior, la excepción contendrá el seguimiento de la pila de la primera línea lanzada. Al capturar una excepción, hay dos opciones en caso de que la excepción deba volver a lanzarse: lanzar simplemente volverá a lanzar la excepción original con la pila original, mientras que habría creado un nuevo seguimiento de la pila.throw e

Finalmente bloques

Java permite que el flujo de control abandone el bloque finally de una sentencia try , independientemente de la forma en que se haya introducido. Esto puede provocar que otra sentencia de flujo de control (como return ) finalice a mitad de la ejecución. Por ejemplo:

int foo () { try { return 0 ; } finalmente { return 1 ; } }            

En el código anterior, la declaración de retorno dentro del bloque try hace que el control lo abandone y, por lo tanto, el bloque finally se ejecuta antes de que ocurra el retorno real. Sin embargo, el bloque finally también realiza un retorno. Por lo tanto, el retorno original que provocó que se ingresara no se ejecuta y el método anterior devuelve 1 en lugar de 0. Hablando informalmente, intenta devolver 0 pero finalmente devuelve 1.

C# no permite ninguna instrucción que permita que el flujo de control abandone el bloque finally de forma prematura, excepto throw . En particular, no se permite return en absoluto, goto no se permite si la etiqueta de destino está fuera del bloque finally , y continue y break no se permiten si el bucle envolvente más cercano está fuera del bloque finally .

Genéricos

En el campo de los genéricos, los dos lenguajes muestran una similitud sintáctica superficial, pero tienen profundas diferencias subyacentes.

Borrado de tipos versus genéricos reificados

Los genéricos en Java son una construcción exclusiva del lenguaje; se implementan únicamente en el compilador. Los archivos de clase generados incluyen firmas genéricas únicamente en forma de metadatos (lo que permite al compilador compilar nuevas clases en función de ellos). El entorno de ejecución no tiene conocimiento del sistema de tipos genéricos; los genéricos no forman parte de la máquina virtual Java (JVM). En cambio, las clases y los métodos genéricos se transforman durante la compilación a través de un proceso denominado borrado de tipos . Durante este proceso, el compilador reemplaza todos los tipos genéricos con su versión original e inserta conversiones/verificaciones de forma adecuada en el código del cliente donde se utilizan el tipo y sus métodos. El código de bytes resultante no contendrá referencias a ningún tipo o parámetro genérico (consulte también Genéricos en Java ).

La especificación del lenguaje Java prohíbe intencionalmente ciertos usos de genéricos; esto es necesario para permitir la implementación de genéricos a través del borrado de tipos y para permitir la compatibilidad de la migración. [72] La investigación para agregar genéricos reificados a la plataforma Java está en curso, como parte del Proyecto Valhalla .

C# se basa en el soporte para genéricos del sistema de ejecución virtual, es decir, no es solo una característica del lenguaje. El lenguaje es simplemente una interfaz para el soporte de genéricos entre lenguajes en el CLR . Durante la compilación, se verifica que los genéricos sean correctos, pero la generación de código para implementar los genéricos se pospone al momento de la carga de la clase. El código del cliente (código que invoca métodos/propiedades genéricos) se compila por completo y puede asumir con seguridad que los genéricos son seguros para los tipos. Esto se llama reificación . En tiempo de ejecución, cuando se encuentra por primera vez un conjunto único de parámetros de tipo para una clase/método/delegado genérico, el cargador/verificador de clases sintetizará un descriptor de clase concreto y generará implementaciones de métodos. Durante la generación de implementaciones de métodos, todos los tipos de referencia se considerarán un solo tipo, ya que los tipos de referencia pueden compartir de manera segura las mismas implementaciones. Esto es simplemente con el propósito de implementar código. Diferentes conjuntos de tipos de referencia seguirán teniendo descriptores de tipo únicos; sus tablas de métodos simplemente apuntarán al mismo código.

La siguiente lista ilustra algunas diferencias entre Java y C# en la gestión de genéricos. No es exhaustiva: [73]

C# permite el uso de genéricos directamente para tipos primitivos. Java, en cambio, permite el uso de tipos encajonados como parámetros de tipo (por ejemplo, en lugar de ). Esto tiene un costo, ya que todos esos valores deben encajonarse o desencajonarse cuando se usan, y todos deben asignarse en el montón. Sin embargo, un tipo genérico se puede especializar con un tipo de matriz de un tipo primitivo en Java, por ejemplo, está permitido. [78] Varias bibliotecas de terceros implementaron las colecciones básicas en Java con matrices primitivas de respaldo para preservar el tiempo de ejecución y la optimización de la memoria que brindan los tipos primitivos. [79]List<Integer>List<int>List<int[]>

Compatibilidad con la migración

El diseño de borrado de tipos de Java estuvo motivado por un requisito de diseño para lograr compatibilidad con la migración (que no debe confundirse con compatibilidad con versiones anteriores ). En particular, el requisito original era " ... debería haber una ruta de migración clara y demostrable para las API de colecciones que se introdujeron en la plataforma Java 2 ". [46] Esto se diseñó para que cualquier nueva colección genérica pudiera pasarse a métodos que esperaban una de las clases de colección preexistentes. [80]

Los genéricos de C# se introdujeron en el lenguaje conservando la compatibilidad total con versiones anteriores, pero no se conservó la compatibilidad total con la migración : el código antiguo (anterior a C# 2.0) se ejecuta sin cambios en el nuevo entorno de ejecución que reconoce los genéricos sin recompilación. En cuanto a la compatibilidad con la migración , se desarrollaron nuevas clases e interfaces de colección genéricas que complementaron las colecciones no genéricas de .NET 1.x en lugar de reemplazarlas. Además de las interfaces de colección genéricas, las nuevas clases de colección genéricas implementan las interfaces de colección no genéricas siempre que sea posible. Esto evita el uso de nuevas colecciones genéricas con métodos preexistentes (que no reconocen los genéricos), si esos métodos están codificados para usar las clases de colección .

Covarianza y contravarianza

Ambos lenguajes admiten la covarianza y la contravarianza. Java tiene una varianza en el sitio de uso que permite que una única clase genérica declare miembros utilizando tanto la covarianza como la contravarianza. C# tiene una varianza en el sitio de definición para las interfaces genéricas y los delegados. La varianza no se admite directamente en las clases, pero se admite a través de su implementación de interfaces variantes. C# también tiene compatibilidad con la covarianza en el sitio de uso para métodos y delegados.

Programación funcional

Cierres

Un cierre es una función en línea que captura variables de su ámbito léxico.

C# admite cierres como métodos anónimos o expresiones lambda con semántica de cierre con todas las funciones . [85] [86]

En Java, las clases internas anónimas seguirán siendo la forma preferida de emular cierres hasta que Java 8 se convierta en el nuevo estándar. Se trata de una construcción más verbosa. Este enfoque también tiene algunas diferencias en comparación con los cierres reales, en particular un acceso más controlado a las variables desde los ámbitos que las encierran: solo se puede hacer referencia a los miembros finales. Sin embargo, Java 8 introduce lambdas que heredan por completo el ámbito actual y, de hecho, no introducen un nuevo ámbito.

Cuando se puede pasar una referencia a un método para su posterior ejecución, surge un problema sobre qué hacer cuando el método tiene referencias a variables/parámetros en su ámbito léxico. Los cierres de C# pueden acceder a cualquier variable/parámetro de su ámbito léxico. En las clases internas anónimas de Java, solo se permiten referencias a miembros finales del ámbito léxico, por lo que se requiere que el desarrollador marque qué variables poner a disposición y en qué estado (posiblemente se requiera boxing).

Lambdas y árboles de expresión

C# y Java cuentan con un tipo especial de cierres en línea llamados lambdas . Se trata de métodos anónimos: tienen una firma y un cuerpo, pero no tienen nombre. Se utilizan principalmente para especificar argumentos con valores de función locales en llamadas a otros métodos, una técnica asociada principalmente con la programación funcional .

A diferencia de Java, C# permite el uso de funciones lambda como una forma de definir estructuras de datos especiales llamadas árboles de expresión. El hecho de que se consideren como una función ejecutable o como una estructura de datos depende de la inferencia de tipo del compilador y del tipo de variable o parámetro al que se les asigna o convierte. Las funciones lambda y los árboles de expresión desempeñan papeles clave en Language Integrated Query (LINQ).

Metadatos

Preprocesamiento, compilación y empaquetado

Espacios de nombres y contenidos de archivos

En C#, los espacios de nombres son similares a los de C++ . A diferencia de los nombres de paquetes en Java, un espacio de nombres no está vinculado de ninguna manera a la ubicación del archivo fuente. Si bien no es estrictamente necesario que la ubicación de un archivo fuente de Java refleje la estructura del directorio del paquete, es la organización convencional.

Ambos lenguajes permiten la importación de clases (por ejemplo, en Java), lo que permite hacer referencia a una clase utilizando únicamente su nombre. A veces, existen clases con el mismo nombre en varios espacios de nombres o paquetes. Se puede hacer referencia a dichas clases utilizando nombres totalmente calificados o importando únicamente clases seleccionadas con nombres diferentes. Para ello, Java permite importar una única clase (por ejemplo, ). C# permite importar clases con un nuevo nombre local utilizando la siguiente sintaxis: . También permite importar especializaciones de clases en forma de .import java.util.*import java.util.Listusing Console = System.Consoleusing IntList = System.Collections.Generic.List<int>

Ambos lenguajes tienen una sintaxis de importación estática que permite usar el nombre corto de algunos o todos los métodos/campos estáticos en una clase (por ejemplo, permitiendo que foo(bar)where foo()pueda importarse estáticamente desde otra clase). C# tiene una sintaxis de clase estática (que no debe confundirse con las clases internas estáticas en Java), que restringe una clase para que solo contenga métodos estáticos. C# 3.0 introduce métodos de extensión para permitir que los usuarios agreguen estáticamente un método a un tipo (por ejemplo, permitiendo que foo.bar()where bar()pueda ser un método de extensión importado que funcione en el tipo de foo ).

El compilador Java de Sun Microsystems requiere que el nombre de un archivo fuente coincida con la única clase pública que contiene, mientras que C# permite múltiples clases públicas en el mismo archivo y no impone restricciones en cuanto al nombre del archivo. C# 2.0 y versiones posteriores permiten dividir una definición de clase en varios archivos mediante el uso de la palabra clave partial en el código fuente. En Java, una clase pública siempre estará en su propio archivo fuente. En C#, los archivos de código fuente y la separación de unidades lógicas no están estrechamente relacionados.

Compilación condicional

Unlike Java, C# implements conditional compilation using preprocessor directives. It also provides a Conditional attribute to define methods that are only called when a given compilation constant is defined. This way, assertions can be provided as a framework feature with the method Debug.Assert(), which is only evaluated when the DEBUG constant is defined. Since version 1.4, Java provides a language feature for assertions, which are turned off at runtime by default but can be enabled using the -enableassertions or -ea switch when invoking the JVM.

Threading and asynchronous features

Both languages include thread synchronization mechanisms as part of their language syntax.

Task-based parallelism for C#

With .NET Framework 4.0, a new task-based programming model was introduced to replace the existing event-based asynchronous model. The API is based around the Task and Task<T> classes. Tasks can be composed and chained.

By convention, every method that returns a Task should have its name postfixed with Async.

public static class SomeAsyncCode{ public static Task<XDocument> GetContentAsync() { HttpClient httpClient = new HttpClient(); return httpClient.GetStringAsync("www.contoso.com").ContinueWith((task) => { string responseBodyAsText = task.Result; return XDocument.Parse(responseBodyAsText); }); }}var t = SomeAsyncCode.GetContentAsync().ContinueWith((task) => { var xmlDocument = task.Result;});t.Start();

In C# 5 a set of language and compiler extensions was introduced to make it easier to work with the task model. These language extensions included the notion of async methods and the await statement that make the program flow appear synchronous.

public static class SomeAsyncCode{ public static async Task<XDocument> GetContentAsync() { HttpClient httpClient = new HttpClient(); string responseBodyAsText = await httpClient.GetStringAsync("www.contoso.com"); return XDocument.Parse(responseBodyAsText); }}var xmlDocument = await SomeAsyncCode.GetContentAsync();// The Task will be started on call with await.

From this syntactic sugar the C# compiler generates a state-machine that handles the necessary continuations without developers having to think about it.

Task-based parallelism for Java

Java supports threads since JDK 1.0. Java offers a high versatility for running threads, often called tasks. This is done by implementing a functional interface (a java.lang.Runnable interface) defining a single void no-args method as demonstrated in the following example:

var myThread = new Thread(() -> { var threadName = Thread.currentThread().getName(); System.out.println("Hello " + threadName);});myThread.start();

Similar to C#, Java has a higher level mechanism for working with threads. Executors can execute asynchronous tasks and also manage a group of subprocesses. All the threads of an ExecutorServices instance are handled in a pool. This ExecutorService instance will be reused under the hood for revenant tasks, so it's possible runs as many concurrent tasks as the programmer wants throughout the life-cycle of the application using a single executor service instance.

This is how the first thread-example looks using executors:

ExecutorService executor = Executors.newSingleThreadExecutor();executor.submit(() -> { var threadName = Thread.currentThread().getName(); System.out.println("Hello " + threadName);});

The ExecutorService instance also supports a Callable interface, another single method interface like Runnable but the signature of the contained method of Callable returns a value. In this way, the lambda expression must also return a value, like the below example of calling a website asynchronously as the C# example.

ExecutorService executor = Executors.newSingleThreadExecutor();Future<String> contentAsync = executor.submit(() -> {  HttpRequest httpReq = HttpRequest.newBuilder() .uri(new URI("www.graalvm.org")) .build();  return HttpClient.newHttpClient() .send(httpReq, BodyHandlers.ofString()) .body();}); var webPageResult = contentAsync.get();

Calling the method get() blocks the current thread and waits until the callable completes before returning the value (in the example, a web page content):

The following example, a method and a class are used. This wrap it's just to be similar to the C# example since Java does not have keywords like async for the method signature.

public static class SomeAsyncCode { static ExecutorService executor = Executors.newSingleThreadExecutor(); public static Future<String> getContentAsync(){ return executor.submit(() -> {  HttpRequest httpReq = HttpRequest.newBuilder() .uri(new URI("www.graalvm.org")) .build(); return HttpClient.newHttpClient() .send(httpReq, BodyHandlers.ofString()) .body(); }); }}var webPageResult = SomeAsyncCode.getContentAsync().get();

Additional features

Numeric applications

To adequately support applications in the field of mathematical and financial computation, several language features exist.[93]

Java's strictfp keyword enables strict floating-point calculations for a region of code. Strict floating-point calculations require that even if a platform offers higher precision during calculations, intermediate results must be converted to single/double. This ensures that strict floating-point calculations return exactly the same result on all platforms. Without strict floating-point, a platform implementation is free to use higher precision for intermediate results during calculation. C# allows an implementation for a given hardware architecture to always use a higher precision for intermediate results if available, i.e. C# does not allow the programmer to optionally force intermediate results to use the potential lower precision of single/double.[94]

Although Java's floating-point arithmetic is largely based on IEEE 754 (Standard for Binary Floating-Point Arithmetic), certain features are unsupported even when using the strictfp modifier, such as Exception Flags and Directed Roundings, abilities mandated by IEEE Standard 754 (see Criticism of Java, Floating point arithmetic).

C# provides a built-in decimal type,[95] which has higher precision (but less range) than the Java/C# double. The decimal type is a 128-bit data type suitable for financial and monetary calculations. The decimal type can represent values ranging from 1.0 × 10−28 to approximately 7.9 × 1028 with 28–29 significant digits.[96] The structure uses C# operator overloading so that decimals can be manipulated using operators such as +, -, * and /, like other primitive data types.

The BigDecimal and BigInteger types provided with Java allow arbitrary-precision representation of decimal numbers and integer numbers, respectively. Java standard library does not have classes to deal with complex numbers.

The BigInteger,[3] and Complex[97] types provided with C# allow representation and manipulation of arbitrary-precision integers and complex numbers, respectively. The structures use C# operator overloading so that instances can be manipulated using operators such as +, -, *, and /, like other primitive data types. C# standard library does not have classes to deal with arbitrary-precision floating point numbers (see software for arbitrary-precision arithmetic).

C# can help mathematical applications with the checked and unchecked operators that allow the enabling or disabling of run-time checking for arithmetic overflow for a region of code.

Language integrated query (LINQ)

C#s Language Integrated Query (LINQ) is a set of features designed to work together to allow in-language querying abilities and is a distinguishing feature between C# and Java.

LINQ consists of these features:

Native interoperability

The Java Native Interface (JNI) feature allows Java programs to call non-Java code. However, JNI does require the code being called to follow several conventions and imposes restrictions on types and names used. This means that an extra adaption layer between legacy code and Java is often needed. This adaption code must be coded in a non-Java language, often C or C++. Java Native Access (JNA) allows easier calling of native code that only requires writing Java code, but comes at a performance cost.

In addition, third party libraries provide Java-Component Object Model (COM) bridging, e.g., JACOB (free), and J-Integra for COM (proprietary).

.NET Platform Invoke (P/Invoke) offers the same ability by allowing calls from C# to what Microsoft terms unmanaged code. Through metadata attributes the programmer can control exactly how the parameters and results are marshalled, thus avoiding the external glue code needed by the equivalent JNI in Java. P/Invoke allows almost complete access to procedural APIs (such as Win32 or POSIX), but limited access to C++ class libraries.

In addition, .NET Framework also provides a .NET-COM bridge, allowing access to COM components as, if they were first-class .NET objects.

C# also allows the programmer to disable the normal type-checking and other safety features of the CLR, which then enables the use of pointer variables. When using this feature, the programmer must mark the code using the unsafe keyword. JNI, P/Invoke, and "unsafe" code are equally risky features, exposing possible security holes and application instability. An advantage of unsafe, managed code over P/Invoke or JNI is that it allows the programmer to continue to work in the familiar C# environment to accomplish some tasks that otherwise would require calling out to unmanaged code. An assembly (program or library) using unsafe code must be compiled with a special switch and will be marked as such. This enables runtime environments to take special precautions before executing potentially harmful code.

Runtime environments

Java (the programming language) is designed to execute on the Java platform via the Java Runtime Environment (JRE). The Java platform includes the Java virtual machine (JVM) and a common set of libraries. The JRE was originally designed to support interpreted execution with final compiling as an option. Most JRE environments execute fully or at least partially compiled programs, possibly with adaptive optimization. The Java compiler produces Java bytecode. Upon execution the bytecode is loaded by the Java runtime and either interpreted directly or compiled to machine instructions and then executed.

C# is designed to execute on the Common Language Runtime (CLR). The CLR is designed to execute fully compiled code. The C# compiler produces Common Intermediate Language instructions. Upon execution the runtime loads this code and compiles to machine instructions on the target architecture.

Examples

Input/output

Example illustrating how to copy text one line at a time from one file to another, using both languages.

Integration of library-defined types

C# allows library-defined types to be integrated with existing types and operators by using custom implicit/explicit conversions and operator overloading as illustrated by the following example:

C# delegates and equivalent Java constructs

Type lifting

Interoperability with dynamic languages

This example illustrates how Java and C# can be used to create and invoke an instance of class that is implemented in another programming language. The "Deepthought" class is implemented using the Ruby programming language and represents a simple calculator that will multiply two input values (a and b) when the Calculate method is invoked. In addition to the conventional way, Java has GraalVM, a virtual machine capable to run any implemented programming language.

Fibonacci sequence

This example illustrates how the Fibonacci sequence can be implemented using the two languages. The C# version takes advantage of C# generator methods. The Java version takes the advantage of Stream interface and method references. Both the Java and the C# examples use K&R style for code formatting of classes, methods and statements.

See also

References

  1. ^ "BigDecimal (Java 2 Platform SE 5.0)". Docs.oracle.com. Retrieved 24 February 2015.
  2. ^ "Mpir.NET". Retrieved 17 July 2015.
  3. ^ a b "BigInteger Struct (System.Numerics)". learn.microsoft.com. Retrieved 14 April 2023.
  4. ^ "Collection (Java 2 Platform SE 5.0)". Docs.oracle.com. Retrieved 20 May 2015.
  5. ^ "String (Java 2 Platform SE 5.0)". Docs.oracle.com. Retrieved 20 May 2015.
  6. ^ "Math – The Commons Math User Guide – Complex Numbers". Retrieved 17 July 2015.
  7. ^ "Date (Java 2 Platform SE 5.0)". Docs.oracle.com. Retrieved 20 May 2015.
  8. ^ "decimal (C# Reference)". Microsoft. Retrieved 30 November 2015.
  9. ^ a b c "The Java Language Environment". Oracle.com. Retrieved 18 August 2013.
  10. ^ a b "Method References (The Java Tutorials > Learning the Java Language > Classes and Objects)". Docs.oracle.com. 28 February 2012. Retrieved 24 February 2015.
  11. ^ Only available in unsafe mode or through IntPtr managed type
  12. ^ The type system is unified by default, unless the compiler is switched into unsafe mode where raw pointers are available as a datatype. Pointers are not derived from object nor does it have implicit conversions to/from the object data type
  13. ^ "org.apache.commons.lang3.tuple (Apache Commons Lang 3.4-SNAPSHOT API)". Commons.apache.org. 15 January 2014. Retrieved 24 February 2015.
  14. ^ a b "Tuple Class (System)". learn.microsoft.com. Microsoft Corporation. Retrieved 20 April 2023.
  15. ^ a b "Tuple types (C# reference)". learn.microsoft.com. Microsoft Corporation. Retrieved 20 April 2023.
  16. ^ "Unsigned Integer Arithmetic API now in JDK 8 (Joseph D. Darcy's Oracle Weblog)". Blogs.oracle.com. Archived from the original on 25 February 2017. Retrieved 24 February 2015.
  17. ^ "Unsafe code, pointer types, and function pointers". learn.microsoft.com. 29 May 2022. Retrieved 14 April 2023.
  18. ^ Joshua Bloch; Neal Gafter (2005). Java puzzlers : traps, pitfalls, and corner cases (5. print. ed.). Upper Saddle River, NJ [u.a.]: Addison-Wesley. p. 36. ISBN 978-0-321-33678-1. The lesson for language designers is that sign extension of byte values is a common source of bugs and confusion. The masking that is required to suppress sign extension clutters programs, making them less readable. Therefore, the byte type should be unsigned.{{cite book}}: CS1 maint: multiple names: authors list (link)
  19. ^ "James Gosling on Java, May 2001". Artima.com. 10 May 2001. Retrieved 24 February 2015.
  20. ^ "Integer (Java Platform SE 8)". Docs.oracle.com. Retrieved 20 April 2023.
  21. ^ "decimal". C# Reference. Microsoft. 19 August 2012.
  22. ^ a b Sestoft, Jon Jagger, Nigel Perry, Peter (2007). "11.1.7 The decimal type". C? annotated standard. Amsterdam: Elsevier/Morgan Kaufmann Publishers. ISBN 978-0-12-372511-0.{{cite book}}: CS1 maint: multiple names: authors list (link)
  23. ^ Mok, Heng Ngee (2003). "9.5. The decimal type". From Java to C? : a developer's guide. Harlow, England: Addison-Wesley. ISBN 978-0-321-13622-0.
  24. ^ "Package java.time (Java Platform SE 8)". docs.oracle.com. Retrieved 20 April 2023.
  25. ^ "How to use the DateOnly and TimeOnly structures". learn.microsoft.com. 12 January 2023. Retrieved 20 April 2023.
  26. ^ "Enum". Internet: .NET Perls. Retrieved 14 November 2016. Performance. Enums are fast. They are almost never a performance concern. They are just syntactic sugar on a type like int, which is also fast. […] Type. An enum has an underlying type. Each time we use the enum, we are using the underlying type. The enum has syntactic sugar on top.
  27. ^ a b Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language". Dare Obasanjo. Archived from the original on 17 December 2001. Retrieved 6 September 2012. In Java, enumerated types are a full-fledged class that means they are typesafe and can be extended by adding methods, fields or even implementing interfaces. Whereas in C#, an enumerated type is simply syntactic sugar around an integral type (typically an int) meaning they cannot be extended and are not typesafe.
  28. ^ Prof. Dr. Gruntz, Dominik (8 April 2005). "Java 5: Taming the Tiger: Syntactic Sugar" (in German). Fachhochschule Aargau, Nordwestschweiz. Archived from the original on 8 July 2012. Retrieved 10 September 2012. Enumerationen sind die heimlichen Sieger von Java 1.5. Nach vielen Beteuerungen durch Sun, Enums seien in Java überflüssig und können einfach nachgebildet werden, wurden sie nun doch eingeführt. Die einfachste Möglichkeit einer Enumeration der Jahreszeiten sieht wie folgt aus … Das Schlüsselwort enum steht für eine spezielle Art von Klasse, die eine Enumeration definiert. … Im Gegensatz zu anderen Programmiersprachen wie C/C++ und C# kann man ihnen per Gleichheitszeichen keine ganzen Zahlen zuordnen.
  29. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: C. An Ever So Slight Feeling of Dèjà Vu: 4. Switch Statement". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 10 September 2012.
  30. ^ "goto (C#)". Msdn.microsoft.com. Retrieved 18 August 2013.
  31. ^ a b "Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle". Java.sun.com. Archived from the original on 27 June 2012. Retrieved 24 February 2015.
  32. ^ How to Use JavaFX in Your Swing Application Archived 4 March 2009 at the Wayback Machine
  33. ^ "Unsafe Code and Pointers (C# Programming Guide)". Microsoft. Retrieved 11 March 2013.
  34. ^ "SortedDictionary<TKey,TValue> Class (System.Collections.Generic)". learn.microsoft.com. Retrieved 20 April 2023.
  35. ^ "SortedSet<T> Class (System.Collections.Generic)". learn.microsoft.com. Retrieved 20 April 2023.
  36. ^ "PriorityQueue<TElement,TPriority> Class (System.Collections.Generic)". learn.microsoft.com. Retrieved 20 April 2023.
  37. ^ "System.Collections.Concurrent Namespace". learn.microsoft.com. Retrieved 20 April 2023.
  38. ^ "foreach, in (C# reference)". Microsoft. 2018. Archived from the original on 12 January 2019. Retrieved 26 January 2019. The foreach statement executes a statement or a block of statements for each element in an instance of the type that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> interface.
  39. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: C. An Ever So Slight Feeling of Dèjà Vu: 6. Collections". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 10 September 2012. The Java collections framework not only has methods that enable one to access unsafe collections in a thread safe manner, but contains thread-safe versions of most of the data structures as well. The Java collections framework has a number of algorithms for manipulating the elements within the data structures including algorithms that can do the following; find the largest element based on some Comparator, find the smallest element, find sublists within a list, reverse the contents of a list, shuffle the contents of a list, creates immutable versions of a collection, performs sorts, and binary searches.
  40. ^ Dare Obasanjo (March 2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: C. An Ever So Slight Feeling of Dèjà Vu: 6. Collections". Dare Obasanjo. Archived from the original on 2 January 2013. Retrieved 10 September 2012. The C# collections framework consists of the classes in the System. Collections and the System.Collections.Generic namespaces. The Systems.Collections namespace contains interfaces and abstract classes that represent abstract data types such as IList, IEnumerable, IDictionary, ICollection, and CollectionBase that enable developers to manipulate data structures independently of how they are actually implemented as long as the data structures inherit from the abstract data types. The System.Collections namespace also contains some concrete implementations of data structures such as ArrayList, Stack, Queue, HashTable and SortedList. All four of the concrete data structure implementations enable one to obtain synchronized wrappers to the collection that allows for access in a thread-safe manner. The System.Collections.Generic namespace has generic implementations of the key data structures in the System.Collections namespace including generic List<T>, Stack<T>, Queue<T>, Dictionary<K,T> and SortedDictionary<K,T> classes.
  41. ^ "javatuples". Retrieved 20 April 2023.
  42. ^ "String interpolation using $". learn.microsoft.com. 8 April 2023. Retrieved 20 April 2023.
  43. ^ "JEP 378: Text Blocks". Retrieved 5 August 2020.
  44. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: D. Now for Something Completely Different: 13. Verbatim Strings". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 11 September 2012.
  45. ^ Eric Fleegal (2004). "Microsoft Visual C++ Floating-Point Optimization". MSDN. Retrieved 1 January 2016.
  46. ^ a b "The Java Community Process(SM) Program – JSRs: Java Specification Requests – detail JSR# 14". Jcp.org. Retrieved 24 February 2015.
  47. ^ "JEP 286: Local-Variable Type Inference". Retrieved 25 April 2018.
  48. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: D. Now for Something Completely Different: 14. Overflow Detection". Archived from the original on 22 September 2012. Retrieved 11 September 2012.
  49. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: An Ever So Slight Feeling of Dèjà Vu: 4. switch Statment [sic]". Archived from the original on 22 September 2012. Retrieved 7 September 2012.
  50. ^ "The try-with-resources Statement (The Java Tutorials > Essential Classes > Exceptions)". Docs.oracle.com. 28 February 2012. Retrieved 24 February 2015.
  51. ^ Extension created for the Java programming language
  52. ^ "Anonymous Types (C# Fundamentals)". learn.microsoft.com. Retrieved 14 April 2013.
  53. ^ "Java SE Specifications". Java.sun.com. Retrieved 24 February 2015.
  54. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: Operator Overloading". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 6 September 2012. Note: Unlike C++, C# does not allow the overloading of the following operators; new, ( ), ||, &&, =, or any variations of compound assignments such as +=, -=, etc. However, compound assignment operators will call overloaded operators, for instance, += would call overloaded +.
  55. ^ "Java News from August, 1998". Cafeaulait.org. Retrieved 24 February 2015.
  56. ^ Sunwold, Corey (25 February 2010). "C# Equivalent to Java's "final"". Corey Sunwold. Archived from the original on 29 November 2012. Retrieved 13 September 2016. There is than one use of the final keyword that C# does not have an equivalent for. When you pass a parameter to a method in Java, and you don't want that parameter's value to change within the scope of that method you can set it as final like this:
  57. ^ "C# – A C# 6.0 Language Preview". learn.microsoft.com. July 2015. Retrieved 14 April 2023.
  58. ^ a b Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: D. Now for Something Completely Different: 15. Explicit Interface Implementation". Dare Obasanjo. Archived from the original on 22 September 2012. Retrieved 11 September 2012.
  59. ^ "in parameter modifier (C# Reference)". Microsoft. 5 March 2018. Archived from the original on 26 January 2019. Retrieved 26 January 2019.
  60. ^ Gosling, James. "The Java® Language Specification". Section 8.4.1. Formal Parameters. Retrieved 5 October 2014.{{cite web}}: CS1 maint: location (link)
  61. ^ Hanselman, Scott (4 April 2008). "How do Extension Methods work and why was a new CLR not required?". Retrieved 29 March 2014. Extension methods are a really nice syntactic sugar. They're not really added to the class, as we can see, but the compiler makes it feel like they are
  62. ^ "Extension Methods (C# Programming Guide)". Microsoft. 2013. Retrieved 29 March 2014. Extension methods are defined as static methods but are called by using instance method syntax
  63. ^ "C# Language Specification Version 4.0". Microsoft. p. 281. Retrieved 10 May 2012. If no part of a partial type declaration contains an implementing declaration for a given partial method, any expression statement invoking it is simply removed from the combined type declaration. Thus the invocation expression, including any constituent expressions, has no effect at run-time. The partial method itself is also removed and will not be a member of the combined type declaration. If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following: […]
  64. ^ "in parameter modifier (C# Reference)". Microsoft. 5 March 2018. Archived from the original on 26 January 2019. Retrieved 26 January 2019. The in keyword causes arguments to be passed by reference. It is like the ref or out keywords, except that in arguments cannot be modified by the called method.
  65. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: D. Now for Something Completely Different: 12. Pass by Reference". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 10 September 2012. In Java the arguments to a method are passed by value meaning that a method operates on copies of the items passed to it instead of on the actual items. In C#, as in C++ and in a sense C, it is possible to specify that the arguments to a method actually be references to the items being passed to the method instead of copies. This feature is particularly useful when one wants to create a method that returns more than one object. In Java trying to return multiple values from a method is unsupported and leads to anomalies like: a method that swaps two numbers that have been the hallmark of freshman computer science classes for years, is impossible to do in Java without resorting to coding tricks.
  66. ^ "Exceptional Exception Filtering". Pluralsight®. Retrieved 24 June 2022.
  67. ^ "The Trouble with Checked Exceptions". Artima.com. Retrieved 24 February 2015.
  68. ^ "Msdn forums – Visual C# Language". Msdn2.microsoft.com. Archived from the original on 20 March 2007. Retrieved 24 February 2015.
  69. ^ Eckel, Bruce. "Does Java need Checked Exceptions?". Archived from the original on 5 April 2002. Retrieved 6 December 2012.
  70. ^ "Failure and Exceptions". Artima.com. 22 September 2003. Retrieved 18 August 2013.
  71. ^ "Checked Exceptions". Shaun Abram. Retrieved 18 August 2013.
  72. ^ "Java SE Specifications". Java.sun.com. Retrieved 24 February 2015.
  73. ^ Angelika Langer. "Java Generics FAQs – Frequently Asked Questions – Angelika Langer Training/Consulting". AngelikaLanger.com. Retrieved 24 February 2015.
  74. ^ Angelika Langer (16 April 2013). "Java Generics FAQs – Under The Hood of the Compiler – Angelika Langer Training/Consulting". AngelikaLanger.com. Retrieved 18 August 2013.
  75. ^ Angelika Langer (16 April 2013). "Java Generics FAQs – Under The Hood of the Compiler – Angelika Langer Training/Consulting". AngelikaLanger.com. Retrieved 18 August 2013.
  76. ^ Angelika Langer (16 April 2013). "Java Generics FAQs – Under The Hood of the Compiler – Angelika Langer Training/Consulting". AngelikaLanger.com. Retrieved 18 August 2013.
  77. ^ Angelika Langer (13 February 2014). "Java Generics FAQs – Type Parameters – Angelika Langer Training/Consulting". AngelikaLanger.com. Retrieved 24 February 2015.
  78. ^ "Generics in C#, Java, and C". Artima.com. Retrieved 24 February 2015.
  79. ^ "trove4j / Trove". Retrieved 30 June 2017.
  80. ^ Neal Gafter (23 September 2004). "Neal Gafter's blog: Puzzling Through Erasure: answer section". Gafter.blogspot.com. Retrieved 18 August 2013.
  81. ^ "Lambda Expressions (The Java Tutorials > Learning the Java Language > Classes and Objects)". Docs.oracle.com. 28 February 2012. Retrieved 24 February 2015.
  82. ^ "Lesson: Aggregate Operations (The Java Tutorials > Collections)". Docs.oracle.com. 28 February 2012. Retrieved 24 February 2015.
  83. ^ Bruno, Eric (7 November 2022). "Curly Braces #6: Recursion and tail-call optimization". Archived from the original on 17 July 2024. Retrieved 17 July 2024.
  84. ^ Grant Richins (11 May 2009). "Tail Call Improvements in .NET Framework 4". MSDN Blogs.
  85. ^ Richter, Jeffrey (April 2001). "An Introduction to Delegates". MSDN Magazine. Retrieved 23 December 2008.
  86. ^ Campbell, Dustin (9 February 2007). "What's in a Closure?". Did it with .NET. Archived from the original on 15 August 2014. Retrieved 23 December 2008.
  87. ^ Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: Metadata Annotations". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 6 September 2012. However a key difference between C# attributes and Java annotations is that one can create meta-annotations (i.e., annotations on annotations) in Java but can not do the same in C#. Developers can create their own custom annotations by creating an annotation type that is similar to an interface except that the keyword @interface is used to define it.
  88. ^ "Element". Msdn.microsoft.com. Retrieved 18 August 2013.
  89. ^ "C# Assembly – Custom Reference Path – Visual C# Kicks". Vcskicks.com. Retrieved 18 August 2013.
  90. ^ "How to do Conditional Compilation with Java". weblogs.java.net. Archived from the original on 5 January 2013. Retrieved 11 August 2015.
  91. ^ Fork-join framework included with Java version 7. "ForkJoinPool (Java Platform SE 7 )". Oracle. Retrieved 17 July 2015.
  92. ^ "Task Parallel Library (TPL)". Msdn.microsoft.com. 18 February 2015. Retrieved 24 February 2015.
  93. ^ "Java for Scientific Computation : Prospects and Problems" (PDF). Pds.ewi.tudelft.nl. Archived from the original (PDF) on 22 September 2007. Retrieved 24 February 2015.
  94. ^ "C# Language Specification Version 5.0". Microsoft. 4.1.6 Floating point types. Retrieved 28 October 2013. Floating-point operations may be performed with higher precision than the result type of the operation. For example, some hardware architectures support an "extended" or "long double" floating-point type with greater range and precision than the double type, and implicitly perform all floating-point operations using this higher precision type. Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects. However, in expressions of the form x*y/z, where the multiplication produces a result that is outside the double range, but the subsequent division brings the temporary result back into the double range, the fact that the expression is evaluated in a higher range format may cause a finite result to be produced instead of an infinity.
  95. ^ "decimal vs. 110". Retrieved 24 February 2015.[dead link]
  96. ^ "C# Language Specification Version 5.0". Microsoft. 4.1.7 The decimal type.
  97. ^ "Complex". Retrieved 24 February 2015.[dead link]
  98. ^ a b Dare Obasanjo (2007). "A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language: C. An Ever So Slight Feeling of Dèjà Vu: 15. Cross Language Interoperability". Dare Obasanjo. Archived from the original on 19 September 2012. Retrieved 10 September 2012. There are a number of ways cross language interoperability works in Java. First of all, there is the Java Native Interface (JNI) … Java also has the ability to interact with distributed objects that use the common object request broker architecture (CORBA) via Java IDL. … C# and the .NET runtime were created with seamless cross-language interoperability as a design goal.
  99. ^ "JNI Types and Data Structures". Docs.oracle.com. Retrieved 9 April 2020.
  100. ^ "Function pointers in C# 9". docs.microsoft.com. Retrieved 27 February 2021.
  101. ^ "Creating C/C++ unions in C#". docs.microsoft.com. Retrieved 27 February 2021.

External links