jueves, 4 de junio de 2009

Aplicaciones con archivos

Aplicaciones con archivos

Crea un archivo desde java, e implementa la entrada de datos al archivo

// CLeercars.cs

using System;
using System.IO;

public class CLeerCars

{

public static void Main (string[] args)
{

StreamReader sr = null;
String str;

try
{

// Crea un flujo desde el archivo doc.txt
sr = new StreamReader("doc.txt");

// Lee del archivo una línea de texto

str = sr.ReadLine();

// Mientras la cadena str no esté vacía
while (str != null)
{

// Muestra la línea leída
Console.WriteLine(str);

// Lee la línea siguiente

str = sr.ReadLine();

}

}

catch(IOException e)
{

Console.WriteLine("Error: " + e.Message);

}

finally
{

// Cierra el archivo
if (sr != null) sr.Close();

}

}

}

El siguiente programa pregunta al usuario si desea sobreescribir los datos existentes en el archivo.

// CEscribirCars2.cs

using System;
using System.IO;

public class CEscribirCars
{

public static void Main ( )
{

StreamWriter sw = null;
String str;

try
{

// Obtiene el nombre del archivo desde la entrada estándar

Console.Write("Nombre del archivo: ");
str = Console.ReadLine();

char resp = 's';
if ( File.Exists(str) )
{

Console.Write("El archivo existe ¿desea sobreescribirlo? (s/n) ");
resp = (char)Console.Read();
// Salta los bytes no leídos del flujo de entrada estándar

Console.ReadLine();

}

if (resp != 's') return;

// Crea un flujo hacia el archivo seleccionado por el usuario.
sw = new StreamWriter(str);

Console.WriteLine("Escriba las líneas de texto a almacenar
en el archivo.\n" + "Finalice cada línea pulsando
la tecla .\n" + "Para finalizar pulse sólo la tecla .\n");

// Lee una línea de la entrada estándar

str = Console.ReadLine();

// Mientras la cadena str no esté vacía
while (str.Length != 0)
{

// Escribe la línea leída en el archivo
sw.WriteLine(str);

// Lee la línea siguiente
str = Console.ReadLine();

}

}

catch(UnauthorizedAccessException e)
{

Console.WriteLine("Error: " + e.Message);

}
catch(IOException e)
{

Console.WriteLine("Error: " + e.Message);

}
finally
{

if (sw != null) sw.Close();

}

}

}

Operaciones básicas en archivos de texto y binarios

Operaciones básicas en archivos de texto y

binarios

Las operaciones básicas con archivos son:

Creacion

Apertura

Lectura

Escritura

Recorrido

Cierre

Archivos de texto.

El manejo de archivos de texto se puede llevar a cabo por medio de dos tipos de flujos: de bytes y de caracteres .

Archivos de Texto con Flujos de Bytes.

Para escribir o leer datos de tipo byte en un archivo se declara un flujo de la clase FileStream , cuyos constructores son:

FileStream (string nombre , FileMode modo )

FileStream (string nombre , FileMode modo , FileAccess acceso )

donde:

nombre es el nombre del archivo en disco, incluyendo la trayectoria.

Ejemplo:

modo es un valor del tipo enumerado FileMode ; puede tomar uno de los siguientes valores:

Valor

Acción

CreateNew

Crea un nuevo archivo. Si el archivo existe, lanzará una excepción del tipo IOException.

Create

Crea un nuevo archivo. Si el archivo existe, será sobreescrito.

Open

Abre un archivo existente.

OpenOrCreate

Abre un archivo, si existe;en caso contrario, se crea un nuevo archivo.

Truncate

Abre un archivo existente y lo trunca a cero bytes de longitud.

Append

Abre un archivo para agregarle datos al final.Si el archivo no existe, lo crea.

acceso es un valor del tipo enumerado FileAccess ; puede tomar uno de los siguientes valores:

Valor

Acción

Read

Permite leer un archivo.

ReadWrite

Permite leer o escribir en el archivo.

Write

permite escribir en el archivo.

Definicion de archivos de texto y binario

Unidad 8 Flujos y Archivos

Definición Archivos Texto Binarios

Definición de archivos de texto y binarios

Los archivos de texto plano son aquellos que están compuestos únicamente por texto sin formato, solo caracteres. estos caracteres se pueden codificar de distintos modos dependiendo de la lengua usada. Se les conoce también como archivos de texto llano o texto simple por carecer de información destinada a generar formatos y tipos de letra.

Un archivo binario es una archivo informático que contiene información de cualquier tipo, codificada en forma binaria para el propósito de almacenamiento y procesamiento de ordenadores.

Muchos formatos binarios contienen partes que pueden ser interpretados como texto. Un archivo binario que solo contiene información de tipo textual sin información sobre el formato del mismo, se dice que es un archivo de texto plano. Habitualmente se contraponen los términos archivo binario y archivo de texto de forma que los primeros no contienen solamente texto.

Excepciones definidas por el usuario

Excepciones definidas por el usuario


En algunos casos puede ser necesario crear tipos de excepción específicas para los errores que ocurren en los programas, por lo que deben crearse clases de excepción definidas por el programador.

Las clases de excepción definidas por el programador deben derivar directa o indirectamente de la clase ApplicationException del espacio de nombres System.

Antes de crear una clase de excepción definida por el programador, es recomendable verificar la existencia de dicha clase en el .NET Framework, y solamente crear clases de excepción para manejar las nuevas excepciones de manera diferente a otros tipos de excepción existentes.

Ejemplo

using System;

class MiExcepción : ApplicationException
{

public MiExcepción() : base("Este es el mensaje de mi excepción.")
{

}

}

class Principal
{

public static void Main()
{

try
{

Principal miObjeto = new Principal();

miObjeto.lanzaException();

Console.WriteLine("Pulse enter para finalizar...");
Console.Read();

}
catch(MiExcepción e)
{

Console.WriteLine(e.Message);
Console.Read();

}

}

public void lanzaException()
{

throw new MiExcepción();

}

}

Gestion de Excepciones

Gestión de Excepciones

La gestión de excepciones de Java lleva la gestión del error en tiempo de ejecución al mundo orientado a objetos. Una excepción de Java es un objeto que describe una condición excepcional que se ha producido en un fragmento de código.

Excepciones no capturadas

Los objetos de excepción los crea automáticamente el intérprete de Java como respuesta a alguna condición excepcional. Como ejemplo tomamos una división por cero. Cuando el intérprete de Java intenta ejecutar la división, observa que el denominador es cero y construye un nuevo objeto de excepción para que se detenga este código y se trate esta condición de error. Una vez detenido el flujo del código en el operador de división, se buscará en la pila de llamadas actual cualquier gestor de excepciones (pila que contiene un registro de las llamadas a método).
Un gestor de excepciones es algo establecido para tratar inmediatamente la condición excepcional. Si no codificamos un gestor de excepciones, se ejecutara el gestor en tiempo de ejecución por defecto. El gestor por defecto imprime el valor String de la excepción y el trazado de la pila del lugar donde se produjo la excepción.

try y catch

A menudo es más elegante y practico manejar nosotros mismos la excepción. Se puede utilizar la palabra clave try para especificar un bloque de código que se debería proteger frente a todas las excepciones. A continuación inmediatamente del bloque try, se incluye la cláusula catch que especifica el tipo de excepción que se desea captar. Veamos estas construcciones sobre el ejemplo anterior de la división:

class Exc {
        public static void main(String args[])  {
               try  {
                       int d = 0;
                       int a = 42;
                } catch (ArithmeticException e)  {
                       System.out.println("división por cero");
                }
        }
}

ArithmeticException es una subclase especial de Exception, que describe más específicamente el tipo de error que se ha producido. El ámbito de la cláusula catch está restringido a las sentencias especificadas por la sentencia try precedente.

Cláusulas catch múltiples

En algunos casos, la misma secuencia de código puede activar más de una condición excepcional. Se pueden tener varias cláusulas catch en una fila. Se inspecciona cada uno de estos tipos de excepción en el orden en que están y el primero que coincida se ejecuta. Las clases de excepción más especificas se colocaran primero, dado que no se alcanzarán las subclases si están después de unas superclase.

throw

La sentencia throw se utiliza para lanzar explícitamente una excepción. En primer lugar, debe obtener un descriptor de una instancia de Throwable, mediante un parámetro en una cláusula catch, o cerrar una utilizando el operador new. esta es la forma general de una sentencia throw:

throw InstanciaThrowable;

El flujo de la ejecución se detiene inmediatamente después de la sentencia throw, y no se llega a la sentencia siguiente. Se inspecciona el bloque try que la engloba más cercano para ver si tiene una cláusula catch cuyo tipo coincida con el de la instancia Throwable. Si la encuentra, el control se transfiere a esa sentencia. Si no, se inspecciona el siguiente bloque try que la engloba, y así sucesivamente, hasta que le gestor de excepción más externo detiene el programa e imprime el trazado de la pila hasta la sentencia throw.

finally

A veces es necesario estar seguro de que se ejecutará un fragmento de código dado independientemente de que excepciones se provocan y capturan. Se puede utilizar la palabra clave finally par identificar dicho bloque de código. Incluso aunque so coincida ninguna de las cláusulas catch, se ejecutará el bloque finally antes del código que está después del final del bloque try completo.

Excepciones

Unidad 7 Excepciones

Definicion de Excepciones

En todo programa existen errores inesperados en tiempo de ejecución, y también errores que no consideramos debido a nuestra propia inexperiencia como programadores. Unos de estos errores ocurren por ejemplo, al intentar acceder a un elemento del arreglo que está fuera del límite de nuestro arreglo, o cuando intentamos acceder a un archivo inexistente, entre otros. Normalmente estos errores interrumpen el flujo de ejecución de nuestros programas, hasta el extremo de provocar la terminación del programa en forma inmediata. Java hace uso de las excepciones1.1 para poder controlar los errores en tiempo de ejecución.

En Java, casi todo los tipos de errores que puedan surgir en tiempo de ejecución lanzan excepciones, es decir, cuando ocurre un error dentro de un método de JAva, este método crea un objeto Exception, dicho objeto contiene información sobre la excepción, que incluye su tipo y el estado del programa cuando ocurrió el error. El sistema de ejecución es el responsable de buscar algún código para manejar el error. El manejo de excepciones en Java sigue una estructura como esta:

 
try {
   //Codigo donde puede ocurrir un error
} 
catch (ExcepcionA ex) { // Que se va a hacer en caso que  se lanze una Excepcion A 
}
...
catch (ExcepcionZ ex) { // Que se va a hacer en caso que se lanze una Excepcion Z 
}
 

Dentro del bloque try{ } viene encerrado la parte del programa que se desea manejar sus excepciones. El código dentro de algún catch (TipoExcepcion e) se ejecuta en caso se que lanze una excepción TipoExcepcion o que pertenezca al grupo TipoExcepcion. El sistema de ejecución Java busca hacia atrás en la pila de llamadas para encontrar el método que esté interesado en manejar una excepción particular.

Clases genericas (plantillas)

Clases Genéricas (Plantillas)

Las clases genéricas encapsulan operaciones que no son específicas de un tipo de datos concreto. El uso más común de las clases genéricas se da con las colecciones, como listas vinculadas, tablas hash, pilas, colas, árboles, etc., en las que las operaciones tales como agregar y quitar elementos de la colección se realizan de forma similar independientemente del tipo de datos que se almacena.

Normalmente, para crear clases genéricas se empieza a partir de una clase concreta existente y se cambian los tipos, uno a uno, por parámetros de tipo hasta que se obtiene un equilibrio óptimo entre generalización y utilidad. Al crear sus propias clases genéricas, se deben tener en cuenta las siguientes consideraciones importantes:

· Tipos que se generalizan como parámetros de tipo.

Como regla general, cuantos más tipos se puedan parametrizar, más flexible y reutilizable será el código. Sin embargo, un exceso de generalización puede producir código difícil de leer y comprender para otros programadores.

Una buena regla consiste en aplicar las restricciones máximas posibles que sigan permitiendo controlar los tipos que es necesario controlar. Por ejemplo, si sabe que la clase genérica está exclusivamente destinada al uso con tipos de referencia, aplique una restricción de clase. Esto evitará el uso imprevisto de la clase con tipos de valor y permitirá utilizar el operador as en T y comprobar si hay valores nulos.

· Factorizar o no el comportamiento genérico en las clases base y las subclases.

Dado que las clases genéricas pueden actuar como clases base, se aplican las mismas consideraciones de diseño que con las clases no genéricas. Vea más adelante las reglas para la herencia de clases base genéricas.

· Implementar o no una o varias interfaces genéricas.

Definición Creación Paquetes Librería

Definición Creación Paquetes Librería

Un paquete en Java es lo que su nombre lo indica, un paquete o conjunto de clases, lógicamente, que tienen cosas en común. Lógicamente un programado puede agruparlas con o sin criterio, pero lo ideal es que se dediquen a funciones especificas. También los paquetes definen una jerarquía de directorios, que permiten igualmente agrupas las clases, cosa de desplegar los archivos mas ordenadamente. También, cuando necesitamos librerías de java, igualmente estamos llamando a una clase especifica o a todo un paquete. Por ejemplo, cuando ponemos esto:

import System.io.*;

estamos importando todas las clases del paquete System.io. Ahora, si hacemos esto:

import org.usach.abo.ABO;

Estamos importando la clase ABO del paquete org.usach.abo.

Para definir un paquete solo tenemos que incluir antes que los paquetes a importar, la definición del paquete al que pertenece la clase, de la forma siguiente:

package org.ruta.del.paquete;

lo que implica que el paquete tiene que estar en la ruta:

org/ruta/del/paquete

Reutilización de la definición de una interfaz

Reutilización de la definición de una interfaz

Un componente se considera reutilizado cuando se introduce en el nuevo esquema junto con toda la información que posee, bien por sí mismo, bien por el esquema en el que se encuentra. Una clase es reutilizada cuando es introducida en un esquema junto con todas sus jerarquías, asociaciones, atributos, eventos, acciones y representación gráfica (si la tuviera). Las acciones, eventos y atributos que la clase encapsula, a su vez, se consideran reutilizados en el modelo.

La reutilización aplicada a componentes de alto nivel, como clases y procesos, proporciona los mayores niveles de automatización. Su contrapartida resulta ser, normalmente, un elevado nivel de incorporación de componentes de bajo nivel (acciones y atributos+variables) que no son utilizados nunca, pero que se encuentran incluidos en el esquema por estar encapsulados en dichas clases o procesos.


Implementación de la definición de una interfaz

Implementación de la definición de una

interfaz

Una interfaz puede heredar de más de una interfaz base, formando jerarquías similares a las de las clases.

Lo que en otras palabras significa que se maneja de la misma forma que las clases base y las clases derivadas, recapitulando, para hacer mas clases derivadas, deben depender de una clase base, tal es el mismo caso con las clases interface

Un ejemplo de cómo se maneja la interface en C++

Ejemplo:

using C=System.Console;

interface IUno
{

void metodoA( ) ;

}

interface IDos
{

int metodoB( ) ;

}

interface IDerivada : IUno , IDos
{

void metodoX ( ) ;

}

// Las clases que implementan a una interfaz derivada deben implementar
// todos los métodos definidos en las interfaces base.

class Implementadora : IDerivada
{

public void metodoA( )
{

C.WriteLine("Soy el método A") ;

}

public int metodoB( )
{

C.WriteLine("Soy el método B ") ;
return 0 ;

}

public void metodoX( )
{

C.WriteLine("Soy el método X ") ;

}

}

class Principal
{

static void Main( )
{

Implementadora ob = new Implementadora( );
ob.metodoA( ) ;
ob.metodoB( ) ;
ob.metodoX( ) ;

}

}

Definicion de interfase

Definición de interfase

Una Intefase es una Clase abstracta llevada al extremo, la cual permite pre-definir el uso de métodos/campos en futuras clases tal como: Una jerarquía de Instrumentos restringido al uso de una Interfase Instrumento, o bien, una estructura de Figuras al uso de la Interfase Figura.

Una Interfase básicamente dice: “Todas las Clases que implementen esta Interfase deben contener su misma estructura”, para definir una Interfase se utiliza el vocablo interface y para especificar que una Clase debe utilizar determinada Interfase se utiliza el vocablo implements.

Al ser utilizada una Interfase en una Clase se esta indicando: “La Interfase dicta la forma/estructura.

Clases abstractas

Clases abstractas

La abstracción es un recurso de la mente (quizás el más característico de nuestra pretendida superioridad respecto del mundo animal). Por su parte, los lenguajes de programación permiten expresar la solución de un problema de forma comprensible simultáneamente por la máquina y el humano. Constituyen un puente entre la abstracción de la mente y una serie de instrucciones ejecutables por un dispositivo electrónico. En consecuencia, la capacidad de abstracción es una característica deseable de los lenguajes artificiales, pues cuanto mayor sea, mayor será su aproximación al lado humano. Es decir, con la imagen existente en la mente del programador. En este sentido, la introducción de las clases en los lenguajes orientados a objetos ha representado un importante avance respecto de la programación tradicional y dentro de ellas, las denominadas clases abstractas son las que representan el mayor grado de abstracción.

De hecho, las clases abstractas presentan un nivel de "abstracción" tan elevado que no sirven para instanciar objetos de ellas. Representan los escalones más elevados de algunas jerarquías de clases y solo sirven para derivar otras clases, en las que se van implementando detalles y concreciones, hasta que finalmente presentan un nivel de definición suficiente que permita instanciar objetos concretos. Se suelen utilizar en aquellos casos en que se quiere que una serie de clases mantengan una cierta característica o interfaz común. Por esta razón a veces se dice de ellas que son solo interfaz

Resulta evidente en el ejemplo de la figura que los diversos tipos de motores tienen características diferentes. Realmente tienen poco en común un motor eléctrico de corriente alterna y una turbina de vapor. Sin embargo, la construcción de una jerarquía en la que todos motores desciendan de un ancestro común, la clase abstracta "Motores", presenta la ventaja de unificar la interfaz. Aunque evidentemente su definición será tan "abstracta", que no pueda ser utilizada para instanciar directamente ningún tipo de motor. El creador del lenguaje dice de ellas que soportan la noción de un concepto general del que solo pueden utilizarse variantes más concretas [2].

Clases abstractas

Una clase abstracta es la que tiene al menos una función virtual pura (como hemos visto, una función virtual es especificada como "pura" haciéndola igual a cero.

Nota: recordemos que las clases que tienen al menos una función virtual (o virtual pura) se denominan clases polimórficas. Resulta por tanto, que todas las clases abstractas son también polimórficas, pero no necesariamente a la inversa.

Una clase abstracta solo puede ser usada como clase base para otras clases, pero no puede ser instanciada para crear un objeto.

Una clase abstracta no puede ser utilizada como argumento o como retorno de una función.

Si puede declararse punteros-a-clase abstracta.

Se permiten referencias-a-clase abstracta, suponiendo que el objeto temporal no es necesario en la inicialización.

Ejemplo


class Figura { // clase abstracta (CA)
point centro;
...
public:
getcentro() { return center; }
mover(point p) { centro = p; dibujar(); }
virtual void rotar(int) = 0; // función virtual pura
virtual void dibujar() = 0; // función virtual pura
virtual void brillo() = 0; // función virtual pura
...
};
...
Figura x; // ERROR: intento de instanciar una CA
Figura* sptr; // Ok: puntero a CA.
Figura f(); // ERROR: función NO puede devolver tipo CA.
int g(Figura s); // ERROR: CA NO puede ser argumento de función
Figura& h(Figura&); // Ok: devuelve tipo "referencia-a-CA"
int h(Figura&); // Ok: "referencia-a-CA" si puede ser argumento



Concepto de polimorfismo

Unidad 6 Polimorfismo y Reutilizacion

Concepto de polimorfismo

En programación orientada a objetos se denomina polimorfismo a la capacidad que tienen los objetos de una clase de responder al mismo mensaje o evento en función de los parámetros utilizados durante su invocación. Un objeto polimórfico es una entidad que puede contener valores de diferentes tipos durante la ejecución del programa.

En la práctica esto quiere decir que un puntero a un tipo puede contener varios tipos diferentes, no solo el creado. De esta forma podemos tener un puntero a un objeto de la clase Trabajador, pero este puntero puede estar apuntando a un objeto subclase de la anterior como podría ser Márketing, Ventas o Recepcionistas (todas ellas deberían ser subclase de Trabajador).

El concepto de polimorfismo se puede aplicar tanto a funciones como a tipos de datos. Así nacen los conceptos de funciones polimórficas y tipos polimórficos. Las primeras son aquellas funciones que pueden evaluarse o ser aplicadas a diferentes tipos de datos de forma indistinta; los tipos polimórficos, por su parte, son aquellos tipos de datos que contienen al menos un elemento cuyo tipo no está especificado.

Se puede clasificar el polimorfismo en dos grandes clases:

  • Polimorfismo dinámico (o polimorfismo paramétrico) es aquél en el que el código no incluye ningún tipo de especificación sobre el tipo de datos sobre el que se trabaja. Así, puede ser utilizado a todo tipo de datos compatible.
  • Polimorfismo estático (o polimorfismo ad hoc) es aquél en el que los tipos a los que se aplica el polimorfismo deben ser explicitados y declarados uno por uno antes de poder ser utilizados.

El polimorfismo dinámico unido a la herencia es lo que en ocasiones se conoce como programación genérica.

También se clasifica en herencia por redefinición de métodos abstractos y por método sobrecargado. El segundo hace referencia al mismo método con diferentes parámetros.

Otra clasificación agrupa los polimorfismo en dos tipos: Ad-Hoc que incluye a su vez sobrecarga de operadores y coerción, Universal (inclusión o controlado por la herencia, paramétrico o genericidad).

Aplicaciones

Aplicaciones de Herencia

Ejemplo de la herencia simple y herencia múltiple

public class Mamifero
   {
 
   private int patas;
   private String nombre;
 
   public void imprimirPatas()
   {
     System.out.println(nombre + " tiene " + patas + " patas\n");
   }
 
   public Mamifero(String nombre, int patas)
   {
     this.nombre = nombre;
     this.patas = patas;
   }
 }
 
 public class Perro extends Mamifero
   {
   public Perro(String nombre){
     super(nombre, 4);
   }
 }
 
 public class Gato extends Mamifero{
   public Gato(String nombre){
     super(nombre, 4);
   }
 }
 
 public class CreaPerro {
   public static void main(String [] args) {
     Perro bobi = new Perro("Bobi");
     bobi.imprimirPatas();   /*Está en la clase mamífero*/
   }
 }

Ejemplo de clases derivadas

// Redef.cs : Ejemplifica la redefinición de campos en clases derivadas.

class Punto
{

public int x;
public int y;

}

class Punto3D : Punto
{

public int x ;
public int y ;
public int z ;

}

class Principal
{

public static void Main( )
{

Punto a = new Punto( );
Punto3D b = new Punto3D( );

a.x = 100 ;
a.y = 200 ;

b.x = 300 ;
b.y = 400 ;
b.z = 500 ;

}

}

Constructores y destructores de clases derivadas

Constructores y destructores en clases derivadas

Constructores en clases derivadas. Al instanciar objetos de clases derivadas se inicia una cadena de invocaciones a constructores en las cuales el constructor de la clase derivada, antes de realizar sus propias tareas, invoca (ya sea implícita o explícitamente) al constructor de su clase base. Similarmente, si la clase base fue derivada de otra clase, el constructor de la clase base debe invocar al constructor de la clase ubicada en el siguiente nivel superior de la jerarquía, y así sucesivamente. El último constructor invocado en la cadena es el constructor de la clase Object, cuyo cuerpo se ejecuta primero. El cuerpo del constructor de la clase derivada se ejecuta al final. El constructor de cada clase base inicializa las variables de instancia que el objeto de la clase derivada hereda.

Destructores en clases derivadas. Cuando remueve de la memoria un objeto de una clase derivada, el recolector de basura invoca al destructor del objeto. Esto inicia una cadena de invocaciones a destructores, en donde el destructor de la clase derivada y los destructores de las clases bases directas e indirectas se ejecutan en orden inverso al que se ejecutaron los constructores, esto es, primero se ejecuta el destructor de la clase derivada y al final se ejecuta el destructor de la clase base ubicada en el nivel superior de la jerarquía. La ejecución de los destructores debe liberar todos los recursos que el objeto adquirió, antes de que el recolector de basura reclame la memoria de ese objeto.

Cuando el recolector de basura invoca al destructor de un objeto de una clase derivada, ese destructor realiza su tarea y después invoca al destructor de la clase base. El proceso se repite hasta que se invoca al destructor de la clase Object.

Clases virtuales y visibilidad

Clases virtuales y visibilidad

Se puede hacer que una clase derivada cambie la implementación de un método de una clase base, manteniendo el nombre del método.

A esta operación de reimplementar un método de una clase base en una clase derivada se le conoce como remplazar (override) el método de la clase base.

Los métodos de la clase base que usan la palabra clave virtual reciben el nombre de métodos virtuales y los de la clase derivada que usan la palabra clave override reciben el nombre de métodos de reeemplazo.

Si no existe un modificador virtual , se dice que el método es un método no virtual.

Para lograr el remplazo deben cumplirse dos requisitos:

  1. El método de la clase base debe declararse con la palabra clave virtual.
  2. El método de la clase derivada debe declararse con la palabra clave override.

La implementación de un método no virtual es invariable. La implementación es la misma tanto si se invoca un método en una instancia de la clase en la que se declaró o en una instancia de una clase derivada. En cambio, la implementación de un método virtual se puede sustituir por clases derivadas. El proceso de sustitución de la implementación de un método virtual heredado es conocido como reemplazamiento del método.

Cuando se invoca a un método virtual, el tipo del objeto se comprueba en tiempo de ejecución para ver si existe un miembro de reemplazo y se realiza una llamada al miembro de reemplazo que está en la clase de mayor derivación, el cual puede ser el miembro original, para ver si no existe ninguna clase derivada que haya reemplazado el miembro.

De forma predeterminada, los métodos son no virtuales. No se puede reemplazar un método no virtual.

El modificador virtual no se puede utilizar con los siguientes modificadores:

static

abstract

override

Las propiedades virtuales funcionan como los métodos abstractos, salvo en lo que se refiere a las diferencias en la sintaxis de las declaraciones e invocaciones.

  • Es incorrecto utilizar el modificador virtual en una propiedad estática.
  • Una propiedad virtual heredada se puede reemplazar en una clase derivada si se incluye una declaración de propiedad que use el modificador override .

Las clases que tienen métodos virtuales son instanciables, esto es, se pueden construir objetos de esas clases.

Para la visibilidad, se maneja la palabra reservada Override, la cual sirve para redefinir un método de alguna otra clase, utilizando el método que se sobrecarga, y escondiendo el método anterior, se le llama también sobrecarga de métodos, y al sobrecargar un método, se “sobrescribe” el método anterior con los atributos del método nuevo.

He aquí un pedazo de código, que demuestra como se manejan las clases virtuales y las sobrecargas o visibilidad

Ejemplo:

// ReempMet.cs : Ejemplifica el reemplazamiento de métodos en clases derivadas.

using C=System.Console;

class A
{

public virtual void x( )
{

Console.WriteLine( "A.x" ) ;

}

}

class B : A
{

public override void x( )
{

Console.WriteLine( "B.x" ) ;

}

}

class Principal
{

static void Main ( )
{

A objeto1 = new A( ) ;

B objeto2 = new B( ) ;

objeto1.x( ) ;

objeto2.x( ) ;

}

}