Compatibilidad y consideraciones C#-Java

En este tema se describen algunas de las similitudes y diferencias principales en cómo se representan y asignan los datos, y se recolectan los elementos no utilizados en Java y en C#.

Tipos de datos compuestos

El concepto de clase como tipo de datos compuesto de campos, métodos y eventos es similar en Java y C#. (La herencia de clases se describe independientemente en el tema titulado Herencia y clases derivadas (C# y Java).) C# introduce el concepto de estructura como tipo de datos compuesto asignado por pila que no admite la herencia. En la mayoría de los otros aspectos, las estructuras son muy similares a las clases. Las estructuras proporcionan una manera ligera de agrupar campos y métodos relacionados para el uso en los bucles de pequeñas dimensiones y otros escenarios donde el rendimiento es crucial.
C# permite crear un método de destructor al que se llama antes de que se destruyan las instancias de una clase. En Java, se puede utilizar un método finalize para contener código que limpia los recursos antes de que se recolecten los elementos no utilizados del objeto. En C#, el que realiza esta función es el destructor de clase. El destructor se parece a un constructor sin los argumentos y con un carácter de tilde delante (~).

Tipos de datos integrados

C# proporciona todos los tipos de datos que están disponibles en Java y agrega compatibilidad para los números sin signo y un nuevo tipo de punto flotante de 128 bits de alta precisión.
Para cada tipo de datos primitivo en Java, la biblioteca de clases principal proporciona una clase contenedora, que lo representa como un objeto de Java. Por ejemplo, la clase Int32 contiene el tipo de datos int y la clase Double contiene el tipo de datos double.
Por otro lado, todos los tipos de datos primitivos en C# son objetos en el espacio de nombres System. Para cada tipo de datos, se proporciona un nombre corto o alias. Por ejemplo, int es el nombre corto correspondiente a System.Int32 y double es la forma abreviada de System.Double.
En la tabla siguiente se proporciona la lista de tipos de datos de C# y sus alias. Como puede ver, los primeros ocho de estos tipos corresponden a los tipos primitivos disponibles en Java. Sin embargo, tenga en cuenta que el tipo boolean de Java se denomina boolen C#.
Nombre corto
Clase .NET
Tipo
Ancho
Intervalo (bits)
byte
Entero sin signo
8
0 a 255
sbyte
Entero con signo
8
-128 a 127
int
Entero con signo
32
-2.147.483.648 a 2.147.483.647
uint
Entero sin signo
32
0 a 4294967295
short
Entero con signo
16
-32.768 a 32.767
ushort
Entero sin signo
16
0 a 65535
long
Entero con signo
64
-922337203685477508 a 922337203685477507
ulong
Entero sin signo
64
0 a 18446744073709551615
float
Tipo de punto flotante de precisión simple
32
-3,402823e38 a 3,402823e38
double
Tipo de punto flotante de precisión doble
64
-1,79769313486232e308 a 1,79769313486232e308
char
Un carácter Unicode
16
Símbolos Unicode utilizados en el texto
bool
Tipo Boolean lógico
8
True o false
object
Tipo base de todos los otros tipos
string
Una secuencia de caracteres
decimal
Tipo preciso fraccionario o integral, que puede representar números decimales con 29 dígitos significativos
128
±1.0 × 10e−28 a ±7.9 × 10e28
Dado que C# representa todos los tipos de datos primitivos como objetos, es posible llamar a un método de objeto de un tipo de datos primitivo. Por ejemplo:
static void Main()
{
    int i = 10;
    object o = i;
    System.Console.WriteLine(o.ToString());
}    


Esto se logra con la ayuda de las conversiones automáticas boxing y unboxing. Para obtener más información, vea Conversión boxing y unboxing (Guía de programación de C#).

Constantes

Java y C# proporcionan la capacidad para declarar una variable cuyo valor se especifica en tiempo de compilación y no se puede cambiar en tiempo de ejecución. Java utiliza el modificador de campo final para declarar este tipo de variable, mientras que C# utiliza la palabra clave const. Además de const, C# proporciona la palabra clave readonly para declarar variables a las que se puede asignar un valor una vez en tiempo de ejecución, ya sea en la instrucción de declaración o en otra parte del constructor. Después de la inicialización, el valor de una variable readonly no puede cambiar. Un escenario en el que las variables readonly son útiles es cuando los módulos que se han compilado independientemente tienen que compartir datos como un número de versión. Si el módulo A se actualiza y se vuelve a compilar con un nuevo número de versión, el módulo B se puede inicializar con ese nuevo valor constante sin tener que volver a compilarlo.

Enumeraciones

Las enumeraciones se utilizan para agrupar constantes con nombres en forma similar a la forma en que se utilizan en C y C++; no están disponibles en Java. En el ejemplo siguiente se define una enumeración Color sencilla.
public enum Color
{
    Green,   //defaults to 0
    Orange,  //defaults to 1
    Red,     //defaults to 2
    Blue     //defaults to 3
}  


También se pueden asignar valores integrales a las enumeraciones, tal como se muestra en la siguiente declaración de enumeración:
public enum Color2
{
    Green = 10,
    Orange = 20,
    Red = 30,
    Blue = 40
}


En el siguiente ejemplo de código se llama al método GetNames del tipo Enum para mostrar las constantes disponibles para una enumeración. Luego, asigna un valor a una enumeración y muestra el valor.
class TestEnums
{
    static void Main()
    {
        System.Console.WriteLine("Possible color choices: ");

        //Enum.GetNames returns a string array of named constants for the enum.
        foreach(string s in System.Enum.GetNames(typeof(Color)))
        {
            System.Console.WriteLine(s);
        }

        Color favorite = Color.Blue;

        System.Console.WriteLine("Favorite Color is {0}", favorite);
        System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
    }
}


Resultado

Possible color choices:
Green
Orange
Red
Blue
Favorite Color is Blue
Favorite Color value is 3

Cadenas

Los tipos de cadena en Java y C# denotan un comportamiento similar con leves diferencias. Ambos tipos de cadena son inmutables, lo que significa que los valores de las cadenas no se pueden cambiar una vez que se han creado las cadenas. En ambos casos, los métodos que parecen modificar el contenido real de una cadena crean en realidad una nueva cadena que se devolverá como resultado, dejando la cadena original sin cambios. El proceso de comparación de los valores de cadena es diferente en C# y Java. Para comparar los valores de cadena en Java, los desarrolladores deben llamar al método equals de un tipo string, mientras que el operador == compara los tipos de referencia de forma predeterminada. En C#, los desarrolladores pueden utilizar los operadores == o != para comparar directamente valores de cadena. Aunque una cadena es un tipo de referencia en C#, los operadores == y != compararán, en forma predeterminada, los valores de las cadenas en lugar de las referencias.
Como en Java, los desarrolladores de C# no deben usar el tipo string para concatenar cadenas con el fin de evitar la sobrecarga de crear nuevas clases de cadenas cada vez que se concatene la cadena. En su lugar, los desarrolladores pueden utilizar la clase StringBuilder, que es funcionalmente equivalente a la clase StringBuffer de Java.

Literales de cadena

C# proporciona la posibilidad de evitar el uso de secuencias de escape como "\t" para la ficha o "\" para los caracteres de barra diagonal inversa dentro de las constantes de cadena. Para ello, simplemente declare la cadena textual mediante el símbolo @ para preceder la asignación del valor de cadena. Los siguientes ejemplos muestran cómo utilizar los caracteres de escape y cómo asignar literales de cadena:
static void Main()
{
    //Using escaped characters:
    string path1 = "\\\\FileShare\\Directory\\file.txt";
    System.Console.WriteLine(path1);

    //Using String Literals:
    string path2 = @"\\FileShare\Directory\file.txt";
    System.Console.WriteLine(path2);
}


Conversión y conversión de tipos

Java y C# siguen reglas similares para la conversión automática y la conversión de tipos de datos.
Al igual que Java, C# admite conversiones de tipo implícitas y explícitas. En el caso de conversiones de ampliación, las conversiones son implícitas. Por ejemplo, la siguiente conversión de int a long es implícita, como en Java:
int int1 = 5;
long long1 = int1;  //implicit conversion


La siguiente es una lista de conversiones implícitas entre los tipos de datos de .NET Framework:
Tipo de origen
Tipo de destino
Byte
short, ushort, int, uint, long, ulong, float, double o decimal
Sbyte
short, int, long, float, double o decimal
Int
long, float, double o decimal
Uint
long, ulong, float, double o decimal
Short
int, long, float, double o decimal
Ushort
int, uint, long, ulong, float, double o decimal
Long
float, double o decimal
Ulong
float, double o decimal
Float
double
Char
ushort, int, uint, long, ulong, float, double o decimal
Puede convertir el tipo de expresiones que desee convertir explícitamente usando la misma sintaxis que en Java:
long long2 = 5483;
int int2 = (int)long2;  //explicit conversion


La tabla siguiente muestra las conversiones explícitas.
Tipo de origen
Tipo de destino
Byte
sbyte o char
Sbyte
byte, ushort, uint, ulong o char
Int
sbyte, byte, short, ushort, uint, ulong o char
Uint
sbyte, byte, short, ushort, int o char
Short
sbyte, byte, ushort, uint, ulong o char
Ushort
sbyte, byte, short o char
Long
sbyte, byte, short, ushort, int, uint, ulong o char
Ulong
sbyte, byte, short, ushort, int, uint, ulong o char
Float
sbyte, byte, short, ushort, int, uint, long, ulong, char o decimal
Double
sbyte, byte, short, ushort, int, uint, long, ulong, char o decimal
Char
sbyte, byte o short
Decimal
sbyte, byte, short, ushort, int, uint, long, ulong, char, float o double

Tipos de referencia y valor

C# admite dos tipos de variables:
  • Tipos de valor
    Estos son los tipos de datos primitivos integrados, como char, int y float, así como también los tipos definidos por el usuario declarados con la estructura.
  • Tipos de referencia
    Clases y otros tipos de datos complejos que se construyen a partir de los tipos primitivos. Las variables de estos tipos no contienen una instancia del tipo, sino sólo una referencia a una instancia.
Si se crean dos variables del tipo de valor, i y j, como se muestra a continuación, i y j son completamente independientes entre sí:
int i = 10;
int j = 20;


Tienen ubicaciones de memoria independiente:
Direcciones de memoria separadas por tipos de valor
Si cambia el valor de una de estas variables, la otra no se verá afectada de forma natural. Por ejemplo, si tiene una expresión como la siguiente, aún no existe ninguna conexión entre las variables:
int k = i;


Es decir, si cambia el valor de i, k permanecerá con el valor que tenía i en el momento de la asignación.
i = 30;

System.Console.WriteLine(i.ToString());  // 30
System.Console.WriteLine(k.ToString());  // 10


Sin embargo, los tipos de referencia actúan de forma diferente. Por ejemplo, podría declarar dos variables de la siguiente forma:
Employee ee1 = new Employee();
Employee ee2 = ee1;


Ahora, puesto que las clases son tipos de referencia de C#, ee1 se conoce como referencia a Employee. La primera de las dos líneas anteriores crea una instancia de Employee en memoria y define ee1 para que haga referencia a ella. Así, cuando se establece ee2 para que sea igual a ee1, el primero contiene un duplicado de la referencia a la clase de la memoria. Si ahora cambia las propiedades de ee2, las propiedades de ee1 reflejan estos cambios, ya que ambas apuntan al mismo objeto de la memoria, tal como aparece a continuación:
Ubicaciones de memoria para tipos de referencia

Conversiones boxing y unboxing

El proceso de convertir el tipo de un valor en el tipo de una referencia se denomina conversión boxing. El proceso inverso, convertir el tipo de una referencia en el tipo de un valor, se denomina conversión unboxing. Esto queda reflejado en el ejemplo de código siguiente:
int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;  // unboxing


Java requiere que esas conversiones se realicen manualmente. Los tipos de datos primitivos se pueden convertir en objetos de clases contenedoras construyendo esos objetos o aplicando la conversión boxing. De igual manera, los valores de los tipos de datos primitivos se pueden extraer de los objetos de las clases contenedoras llamando a un método adecuado de estos objetos, o realizar una conversión unboxing. Para obtener más información acerca de las conversiones boxing y unboxing, vea Conversión boxing y unboxing (Guía de programación de C#).

Conversión boxing y unboxing (Guía de programación de C#)

La conversión boxing es el proceso de convertir Tipos de valores (Referencia de C#) en el tipo object o en cualquier tipo de interfaz implementado por este tipo de valor. Cuando CLR aplica la conversión boxing a un tipo de valor, ajusta el valor dentro de un elemento System.Object y lo almacena en el montón administrado. La conversión unboxing extrae el tipo de valor del objeto. En el ejemplo siguiente, se aplica la conversión boxing a la variable de tipo entero i y esta se asigna al objeto o.
int i = 123;
object o = (object)i;  // boxing


A continuación, se puede aplicar la conversión unboxing al objeto y asignarlo a la variable de entero i:
o = 123;
i = (int)o;  // unboxing


Rendimiento

Con relación a las asignaciones simples, las conversiones boxing y unboxing son procesos que consumen muchos recursos. Cuando se aplica la conversión boxing a un tipo de valor, se debe asignar y construir un objeto completamente nuevo. En menor grado, la conversión de tipos requerida para aplicar la conversión unboxing también es costosa. Para obtener más información, vea Rendimiento (Guía de programación de C#).

Boxing

La conversión boxing se utiliza para almacenar tipos de valor en el montón de recolección de elementos no utilizados. Boxing es una conversión implícita de Tipos de valores (Referencia de C#) al tipo object o a cualquier tipo de interfaz implementado por este tipo de valor. Al aplicar la conversión boxing a un tipo de valor se asigna una instancia de objeto en el montón y copia el valor en el nuevo objeto.
Considere la siguiente declaración de una variable de tipo de valor:
int i = 123;


La siguiente instrucción aplica implícitamente la operación de boxing sobre la variable i:
object o = i;  // Implicit boxing


El resultado de esta instrucción es crear una referencia de objeto o en la pila que hace referencia a un valor del tipo int en el montón. Este valor es una copia del valor del tipo de valor asignado a la variable i. La diferencia entre las dos variables, i y o, se muestra en la siguiente ilustración.
Conversión Boxing
Gráfico BoxingConversion
También es posible realizar la conversión boxing de manera explícita, tal como se muestra en el ejemplo siguiente, pero ésta nunca es necesaria:
int i = 123;
object o = (object)i;  // explicit boxing


Descripción

Este ejemplo convierte una variable de entero i en un objeto o mediante la conversión boxing. A continuación, el valor almacenado en la variable ise cambia de 123 a 456. El ejemplo muestra que el tipo de valor original y el objeto empaquetado usan ubicaciones de memoria independientes y, por consiguiente, pueden almacenar valores diferentes.

Ejemplo

class TestBoxing
{
    static void Main()
    {
        int i = 123;
        object o = i;  // Implicit boxing

        i = 456;  // Change the contents of i

        System.Console.WriteLine("The value-type value = {0}", i);
        System.Console.WriteLine("The object-type value = {0}", o);
    }
}
/* Output:
    The value-type value = 456
    The object-type value = 123
*/


El ejemplo siguiente muestra un caso de conversión unboxing no válida y la excepción InvalidCastExceptionresultante. Si se utiliza try y catch, se muestra un mensaje de error cuando se produce el error.
class TestUnboxing
{
    static void Main()
    {
        int i = 123;
        object o = i;  // implicit boxing

        try
        {
            int j = (short)o;  // attempt to unbox

            System.Console.WriteLine("Unboxing OK.");
        }
        catch (System.InvalidCastException e)
        {
            System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
        }
    }
}


Este programa produce el siguiente resultado:
Specified cast is not valid. Error: Incorrect unboxing.
Si cambia la instrucción:
int j = (short) o;
a:
int j = (int) o;
la conversión se realizará y se obtendrá el resultado:
Unboxing OK.

Unboxing

Unboxing es una conversión explícita del tipo object a un tipo de valor o de un tipo de interfaz a un tipo de valor que implementa la interfaz. Una operación de unboxing consiste en:
  • Comprobar la instancia de objeto para asegurar que se trata de un valor de conversión boxing del tipo de valor dado.
  • Copiar el valor de la instancia en la variable de tipo de valor.
Las siguientes instrucciones muestran las operaciones de boxing y unboxing:
int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;  // unboxing


En la ilustración siguiente se muestra el resultado de las instrucciones anteriores.
Conversión Unboxing
Gráfico de conversión UnBoxing
Para que la conversión Unboxing de tipos de valores sea correcta en tiempo de ejecución, el elemento al que se aplica debe ser una referencia a un objeto creado previamente mediante la conversión Boxing de una instancia de dicho tipo de valor. Si se intenta aplicar la conversión unboxing anull o una referencia a un tipo de valor incompatible, se producirá una excepción InvalidCastException.

Herencia y clases derivadas (C# y Java)

La funcionalidad de una clase existente se puede extender al crear una nueva clase que se deriva de ella. La clase derivada hereda las propiedades de la clase base y es posible agregar o reemplazar métodos y propiedades según sea necesario.
En C#, el operador :, que equivale a extends e implements en Java, define la herencia e implementación de interfaces. La clase base siempre debe estar en el extremo izquierdo en la declaración de clase.
Como Java, C# no admite herencia múltiple, lo que significa que las clases no pueden heredar más de una clase. Sin embargo, se pueden utilizar interfaces para ese propósito, de la misma manera que en Java.
El código siguiente define una clase denominada CoOrds con dos variables miembro privadas x e y que representan la posición del punto. Se tiene acceso a estas variables mediante propiedades denominadas X e Y, respectivamente:
public class CoOrds
{
    private int x, y;

    public CoOrds()  // constructor
    {
        x = 0;
        y = 0;
    }

    public int X
    {
        get { return x; }
        set { x = value; }
    }

    public int Y
    {
        get { return y; }
        set { y = value; }
    }
}


Una nueva clase, denominada ColorCoOrds, se deriva de la clase CoOrds del siguiente modo:
public class ColorCoOrds : CoOrds


Luego, ColorCoOrds hereda todos los campos y métodos de la clase base, a la cual se pueden agregar nuevos campos y métodos para proporcionar características adicionales en la clase derivada, según sea necesario. En este ejemplo, se agrega un miembro privado y descriptores de acceso para agregar color a la clase:
public class ColorCoOrds : CoOrds
{
    private System.Drawing.Color screenColor;


    public ColorCoOrds()  // constructor
    {
        screenColor = System.Drawing.Color.Red;
    }

    public System.Drawing.Color ScreenColor
    {
        get { return screenColor; }
        set { screenColor = value; }
    }
}


El constructor de la clase derivada llama implícitamente al constructor de la clase base o la superclase, en terminología de Java. En caso de herencia, se llama a todos los constructores de clase base antes que a los constructores de la clase derivada en el orden en que las clases aparecen en la jerarquía de clases.

Convertir un tipo a una clase base

Como en Java, no se puede utilizar una referencia a una clase base para tener acceso a los miembros y métodos de una clase derivada, aunque la referencia de la clase base pueda contener una referencia válida a un objeto del tipo derivado.
Implícitamente, se puede hacer referencia a una clase derivada con una referencia al tipo derivado:
ColorCoOrds color1 = new ColorCoOrds();
CoOrds coords1 = color1;


En este código, la referencia de clase base, coords1, contiene una copia de la referencia color1.

La palabra clave base

Se puede tener acceso a los miembros de clase base en una subclase incluso cuando los miembros de base se reemplazan en la superclase utilizando la palabra clave base. Por ejemplo, puede crear una clase derivada que contenga un método con la misma firma que la clase base. Si se precede ese método con la palabra clave new, se indica que se trata de un método totalmente nuevo que pertenece a la clase derivada. También se podría proporcionar un método para tener acceso al método original de la clase base con la palabra clave base.
Por ejemplo, supongamos que la clase base CoOrds tuviera un método denominado Invert() que intercambia las coordenadas x e y. Se podría proporcionar un sustituto para este método en la clase derivada ColorCoOrds con un código como éste:
public new void Invert()
{
    int temp = X;
    X = Y;
    Y = temp;
    screenColor = System.Drawing.Color.Gray;
}


Como se puede observar, este método intercambia x e y, luego establece el color del punto en gris. Se podría proporcionar acceso a la implementación base para este método creando otro método en ColorCoOrds, como el de este ejemplo:
public void BaseInvert()
{
    base.Invert();
}


A continuación, se invoca el método base en un objeto ColorCoOrds mediante una llamada al método BaseInvert().
ColorCoOrds color1 = new ColorCoOrds();
color1.BaseInvert();


Recuerde que se obtendría el mismo efecto si se asignara una referencia de la clase base a una instancia de ColorCoOrds y, a continuación, se tuviera acceso a sus métodos:
CoOrds coords1 = color1;
coords1.Invert();


Seleccionar los constructores

Los objetos de clase base siempre se construyen antes que cualquier clase derivada. De esta forma, el constructor de la clase base se ejecuta antes que el constructor de la clase derivada. Si la clase base tiene más de un constructor, la clase derivada puede decidir a qué constructor se va a llamar. Por ejemplo, podría modificar la clase CoOrds para agregar un segundo constructor, del siguiente modo:
public class CoOrds
{
    private int x, y;

    public CoOrds()
    {
        x = 0;
        y = 0;
    }

    public CoOrds(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}


Luego, podría cambiar la clase ColorCoOrds para utilizar uno de los constructores disponibles mediante la palabra clave base:
public class ColorCoOrds : CoOrds
{
    public System.Drawing.Color color;

    public ColorCoOrds() : base ()
    {
        color = System.Drawing.Color.Red;
    }

    public ColorCoOrds(int x, int y) : base (x, y)
    {
        color = System.Drawing.Color.Red;
    }
}


En Java, esta funcionalidad se implementa con la palabra clave super.

Reemplazar el método

Una clase derivada puede reemplazar el método de una clase base si se proporciona una nueva implementación del método declarado. Una diferencia importante entre Java y C# es que, de forma predeterminada, los métodos de Java se marcan como virtuales, mientras que en C# los métodos se deben marcar explícitamente como virtuales con el modificador virtual. Los descriptores de acceso de propiedades, así como los métodos, se pueden reemplazar de manera muy similar.

Métodos virtuales

Un método que será reemplazado en una clase derivada se declara con el modificador virtual. En una clase derivada, el método reemplazado se declara con el modificador override.
El modificador override denota un método o propiedad de una clase derivada que reemplaza un método o propiedad con el mismo nombre y firma en la clase base. El método base, que será reemplazado, se debe declarar como virtualabstract u override: no es posible reemplazar un método no virtual o estático de esta forma. El método o la propiedad reemplazados y aquellos que se reemplazan deben tener los mismos modificadores de nivel de acceso.
El ejemplo siguiente muestra un método virtual denominado StepUp que es reemplazado en una clase derivada con el modificador que lo reemplaza:
public class CountClass 
{
    public int count; 

    public CountClass(int startValue)  // constructor
    {
        count = startValue;
    }

    public virtual int StepUp()
    {
        return ++count;
    }
}

class Count100Class : CountClass 
{
    public Count100Class(int x) : base(x)  // constructor 
    {
    }

    public override int StepUp()
    {
        return ((base.count) + 100);
    }
}

class TestCounters
{
    static void Main()
    {
        CountClass counter1 = new CountClass(1);
        CountClass counter100 = new Count100Class(1);

        System.Console.WriteLine("Count in base class = {0}", counter1.StepUp());
        System.Console.WriteLine("Count in derived class = {0}", counter100.StepUp());
    } 
}


Cuando se ejecuta este código, se observa que el constructor de la clase derivada utiliza el cuerpo del método proporcionado en la clase base, lo que permite inicializar el recuento de miembros sin duplicar el código. Éste es el resultado:
Count in base class = 2
Count in derived class = 101

Clases abstractas

Una clase abstracta declara uno o más métodos o propiedades como abstractos. La clase que declara dichos métodos no les proporciona una implementación, aunque una clase abstracta también puede contener métodos no abstractos, es decir, métodos para los que se ha proporcionado una implementación. No se puede crear directamente una instancia de una clase abstracta; sólo se puede crear una instancia de una clase derivada. Estas clases derivadas deben proporcionar implementaciones para todos los métodos y propiedades abstractos, mediante la palabra clave override, a menos que el miembro derivado se declare abstracto.
El ejemplo siguiente declara una clase abstracta Employee. También se crea una clase derivada denominada Manager, que proporciona una implementación del método abstracto Show() definido en la clase Employee:
public abstract class Employee 
{ 
    protected string name;

    public Employee(string name)  // constructor
    { 
        this.name = name;
    }

    public abstract void Show();  // abstract show method
}

public class Manager: Employee
{ 
    public Manager(string name) : base(name) {}  // constructor

    public override void Show()  //override the abstract show method
    {
        System.Console.WriteLine("Name : " + name);
    }
}

class TestEmployeeAndManager
{ 
    static void Main()
    { 
        // Create an instance of Manager and assign it to a Manager reference:
        Manager m1 = new Manager("H. Ackerman");
        m1.Show();

        // Create an instance of Manager and assign it to an Employee reference:
        Employee ee1 = new Manager("M. Knott");
        ee1.Show();  //call the show method of the Manager class
    } 
}


Este código invoca la implementación del método Show() proporcionado por la clase Manager e imprime el nombre del empleado en pantalla. Éste es el resultado:
Name : H. Ackerman
Name : M. Knott

Interfaces

Una interfaz es un tipo de clase esqueleto que contiene firmas de método pero no incluye ninguna implementación de método. De esta manera, las interfaces son como clases abstractas que contienen sólo métodos abstractos. Las interfaces de C# son muy similares a las de Java y funcionan de manera muy similar.
Todos los miembros de una interfaz son públicos por definición y una interfaz no puede contener constantes, campos (miembros de datos privados), constructores, destructores ni ningún tipo de miembro estático. El compilador generará un error si se especifica un modificador para los miembros de una interfaz.
Las clases se pueden derivar de una interfaz para implementar esa interfaz. Estas clases derivadas deben proporcionar implementaciones para todos los métodos de la interfaz, a menos que la clase derivada se declare abstracta.
Una interfaz se declara de forma idéntica en Java. En una definición de interfaz, una propiedad indica sólo su tipo y si es de sólo lectura, sólo escritura o de lectura y escritura únicamente por medio de las palabras clave get y set. La interfaz siguiente declara una propiedad de sólo lectura:
public interface ICDPlayer
{ 
    void Play();  // method signature
    void Stop();  // method signature

    int FastForward(float numberOfSeconds);

    int CurrentTrack  // read-only property
    {
        get;
    } 
}


Una clase se puede heredar de esta interfaz utilizando dos puntos, en lugar de la palabra clave implements de Java. La clase que se implementa debe proporcionar definiciones para todos los métodos y cualquier descriptor de acceso de la propiedad necesario, del siguiente modo:
public class CDPlayer : ICDPlayer
{
    private int currentTrack = 0;

    // implement methods defined in the interface
    public void Play()
    {
        // code to start CD...
    }

    public void Stop()
    {
        // code to stop CD...
    }

    public int FastForward(float numberOfSeconds)
    {
        // code to fast forward CD using numberOfSeconds...

        return 0;  //return success code
    }

    public int CurrentTrack  // read-only property
    { 
        get 
        { 
            return currentTrack; 
        } 
    }

    // Add additional methods if required...
}


Implementar múltiples interfaces

Una clase puede implementar múltiples interfaces mediante la sintaxis siguiente:
public class CDAndDVDComboPlayer : ICDPlayer, IDVDPlayer


Si una clase implementa más de una interfaz donde hay ambigüedad en los nombres de los miembros, se resuelve utilizando el calificador completo del nombre de la propiedad o método. Es decir, la clase derivada puede resolver el conflicto si se utiliza el nombre completo del método para indicar a qué interfaz pertenece, como en ICDPlayer.Play().
Fuente:
https://msdn.microsoft.com/es-bo/library/ms228360(v=vs.90).aspx
https://msdn.microsoft.com/es-bo/library/yz2be5wk(v=vs.90).aspx
https://msdn.microsoft.com/es-bo/library/ms228387(v=vs.90).aspx

Comentarios

Entradas populares de este blog

Convierte tus imagenes y texto en ascii