InicioArticulos y noticiasBases de datosProgramaciónForosInternetServiciosContratacionEmail
Tutorial C#
Introduccion a C#
Programacion con C#
Fundamentos de C#
Operadores
Instrucciones
Estrcuturas de control.
Definición de clases
Creación de objetos
Herencia y métodos virtuales
Polimorfismo
Ocultación de miembros
Miembros de tipo
Encapsulación
Espacios de nombres
Importación de espacios de nombres
Espacio de nombres distribuidos
Variables y tipos de datos
Tablas unidimensionales
Tablas multidimensionales
La clase System.Array
Cadenas de texto
Constantes
Orden de inicialización de variables
Métodos
Métodos externos
Constructores
Destructores
Propiedades
Indizadores
Redefinición de operadores
Delegados y eventos
La clase MulticastDelegate
Llamadas asíncronas
Implementación interna de los delegados
Eventos
Estructuras
Boxing y unboxing
Constructores de estructuras
Enumeraciones
Interfaces
Excepciones
Otras instrucciones
Atributos
Definición de nuevos atributos
Lectura de atributos en tiempo de ejecución
Atributos de compilación
Pseudoatributos
Código inseguro
Definición de punteros
Manipulación de punteros
Operadores relacionados con código inseguro
Fijación de variables apuntadas
Novedades de C# 2.0
Genéricos
Tipos parciales
Iteradores
Mejoras en la manipulación de delegados
Tipos anulables
Modificadores de visibilidad de bloques get y set
Clases estáticas
Referencias a espacios de nombres
Supresión temporal de avisos
Atributos condicionales
Incrustación de tablas en estructuras
Modificaciones en el compilador
También puedes ver ...
Creando cadenas de conexión ConnectionStrings en .net 3.5 o visual Studio 2008 usando SqlConnectionStringBuilder
Migración de WSE a WCF (Web Services de 2005 a 2008)
Generar archivos PDF con C#
TransactionScope - Simplificando el trabajo con transacciones.
LinQ To SQL - Un ejemplo sencillo
Cómo activar Intelisense (autocompletado) en el Web.config
Leer un campo XML de SQL Server 2005 con C#
TripleDES - Un ejemplo practico en C#
Mostrar un GridView dentro de un DataList
String.Format

Afiliados
La Web del programador
MundoProgramacion


 

Ocultación de miembros

     Hay ocasiones en las que puede resultar interesante usar la herencia únicamente como mecanismo de reutilización de código pero no necesariamente para reutilizar miembros. Es decir, puede que interese heredar de una clase sin que ello implique que su clase hija herede sus miembros tal cuales sino con ligeras modificaciones.

     Esto puede muy útil al usar la herencia para definir versiones especializadas de clases de uso genérico. Por ejemplo, los objetos de la clase System.Collections.ArrayList  incluida en la BCL pueden almacenar cualquier número de objetos System.Object, que al ser la clase primigenia ello significa que pueden almacenar objetos de cualquier tipo. Sin embargo, al recuperarlos de este almacén genérico se tiene el  problema de que los métodos que para ello se ofrecen devuelven objetos System.Object, lo que implicará que muchas veces haya luego que reconvertirlos a su tipo original mediante downcasting para poder así usar sus métodos específicos. En su lugar, si sólo se va a usar un ArrayList para almacenar objetos de un cierto tipo puede resultar más cómodo usar un objeto de alguna clase derivada de ArrayList cuyo método extractor de objetos oculte al heredado de ArrayList y devuelva directamente objetos de ese tipo.

     Para ver más claramente cómo hacer la ocultación, vamos a tomar el siguiente ejemplo donde se deriva de una clase con un método void F() pero se desea que en la clase hija el método que se tenga sea de la forma int F():


class Padre
{
 public void F()
 {}
}
class Hija:Padre
{
 public int  F()
 {return 1;}
}

    Como en C# no se admite que en una misma clase hayan dos métodos que sólo se diferencien en sus valores de retorno, puede pensarse que el código anterior producirá un error de compilación. Sin embargo, esto no es así sino que el compilador lo que hará será quedarse únicamente con la versión definida en la clase hija y desechar la heredada de la clase padre. A esto se le conoce como ocultación de miembro ya que hace desparacer en la clase hija el miembro heredado, y cuando al compilar se detecte se generará el siguiente de aviso (se supone que clases.cs almacena el código anterior):

clases.cs(9,15): warning CS0108: The keyword new is required on 'Hija.F()' because it hides inherited member 'Padre.F()'

     Como generalmente cuando se hereda interesa que la clase hija comparta los mismos miembros que la clase padre (y si acaso que añada miembros extra), el compilador  emite el aviso anterior para indicar que no se está  haciendo lo habitual. Si queremos evitarlo hemos de preceder la definición del método ocultador de la palabra reservada new para así indicar explícitamente que queremos ocultar el F() heredado:


class Padre
{
 public void F()
 {}
}
class Hija:Padre
{
 new public int F()
 {return 1;}
}

     En realidad la ocultación de miembros no implica los miembros ocultados tengan que ser métodos, sino que también pueden ser campos o cualquiera de los demás tipos de miembro que en temas posteriores se verán. Por ejemplo, puede que se desee que un campo X de tipo int esté disponible en la clase hija como si fuese de tipo string.

     Tampoco implica que los miembros métodos ocultados tengan que diferenciarse de los métodos ocultadores en su tipo de retorno, sino que pueden tener exactamente su mismo tipo de retorno, parámetros y nombre. Hacer esto puede dar lugar a errores muy sutiles como el incluido en la siguiente variante de la clase Trabajador donde en vez de redefinirse Cumpleaños() lo que se hace es ocultarlo al olvidar incluir el override:


  using System;
  class Persona
  {
     // Campo de cada objeto Persona que almacena su nombre
     public string Nombre;     
     // Campo de cada objeto Persona que almacena su edad
     public int Edad;              
     // Campo de cada objeto Persona que almacena su NIF
     public string NIF;            
    
     // Incrementa en uno la edad del objeto Persona
     public virtual void Cumpleaños()
     {
     Console.WriteLine("Incrementada edad de persona");
     }
 
    // Constructor de Persona
     public Persona (string nombre, int edad, string nif)
     {
      Nombre = nombre;
      Edad = edad;
      NIF = nif;
     }
  }
 
  class Trabajador: Persona
 
  {  // Campo de cada objeto Trabajador que almacena cuánto gana
     int Sueldo;
 
     Trabajador(string nombre, int edad, string nif, int sueldo)
         : base(nombre, edad, nif)
     { // Inicializamos cada Trabajador en base al constructor de Persona
     Sueldo = sueldo;
     }
 
    public Cumpleaños()
     {
     Edad++;
     Console.WriteLine("Incrementada edad de trabajador");
     }         
 
     public static void Main()
     {
        Persona p = new Trabajador("Josan", 22, "77588260-Z", 100000);
        p.Cumpleaños();     
        // p.Sueldo++; //ERROR: Sueldo no es miembro de Persona
     }
  }

     Al no incluirse override se ha perdido la capacidad de polimorfismo, y ello puede verse en que la salida que ahora mostrara por pantalla el código:

 Incrementada edad de persona

    Errores de este tipo son muy sutiles y podrían ser difíciles de detectar. Sin embargo, en C# es fácil  hacerlo gracias a que el compilador emitirá el mensaje de aviso ya visto por haber hecho la ocultación sin new. Cuando el programador lo vea podrá añadir new para suprimirlo si realmente lo que quería hacer era ocultar, pero si esa no era su intención así sabrá que tiene que corregir el código (por ejemplo, añadiendo el override olvidado)

     Como su propio nombre indica, cuando se redefine un método se cambia su definición original y por ello las llamadas al mismo ejecutaran dicha versión aunque se hagan a través de variables de la clase padre que almacenen objetos de la clase hija  donde se redefinió. Sin embargo, cuando se oculta un método no se cambia su  definición en la clase padre sino sólo en la clase hija, por lo que las llamadas al mismo realizadas a través de variables de la clase padre ejecutarán la versión de dicha clase padre y las realizadas mediante variables de la clase hija ejecutarán la versión de la clase hija.

     En realidad el polimorfismo y la ocultación no son conceptos totalmente antagónicos, y aunque no es válido definir métodos que simultáneamente tengan los modificadores override y new ya que un método ocultador es como si fuese la primera versión que se hace del mismo (luego no puede redefinirse algo no definido), sí que es posible combinar new y virtual para definir métodos ocultadores redefinibles. Por ejemplo:


using System;
class A
{
 public virtual void F() { Console.WriteLine("A.F"); }
}
class B: A
{
 public override void F() { Console.WriteLine("B.F"); }
}
class C: B
{
 new public virtual void F() { Console.WriteLine("C.F"); }
}
class D: C
{
 public override void F() { Console.WriteLine("D.F"); }
}
class Ocultación
{
  public static void Main()
  {
   A a = new D();
   B b = new D();
   C c = new D();
   D d = new D();
   a.F();
   b.F();
   c.F();
   d.F();
  }
}

     La salida por pantalla de este programa es:

  B.F
  B.F
  D.F
  D.F

    Aunque el verdadero tipo de los objetos a cuyo método se llama en Main() es D, en las dos primeras llamadas se llama al F() de B. Esto se debe a que la redefinición dada en B cambia la versión de F() en A por la suya propia, pero la ocultación dada en C hace que para la redefinición que posteriormente se da en D se considere que la versión original de F() es la dada en C y ello provoca que no modifique la versiones de dicho método dadas en A y B (que, por la redefinición dada en B, en ambos casos son la versión de B)

    Un truco mnemotécnico que puede ser útil para determinar a qué versión del método se llamará en casos complejos como el anterior consiste en considerar que el mecanismo de polimorfismo funciona como si buscase el verdadero tipo del objeto a cuyo método se llama descendiendo en la jerarquía de tipos desde el tipo de la variable sobre la que se aplica el método y de manera que si durante dicho recorrido se llega a alguna versión del método con new se para la búsqueda y se queda con la versión del mismo incluida en el tipo recorrido justo antes del que tenía el método ocultador.

    Hay que tener en cuenta que el grado de ocultación que proporcione new depende del nivel de accesibilidad del método ocultador, de modo que si es privado sólo ocultará dentro de la clase donde esté definido. Por ejemplo, dado:


using System;
class A
{
 // F() es un método redefinible
 public virtual void F()     
 {
  Console.WriteLine("F() de A");
 } 
}
class B: A
{
  // Oculta la versión de F() de A sólo dentro de B
  new private void F() {} 
}
 
class C: B
{
 // Válido, pues aquí sólo se ve el F() de A
 public override void F()
 {
  base.F();
  Console.WriteLine("F() de B");
 }
 public static void Main()
 {
  C obj = new C();
  obj.F();
 }
}

La salida de este programa por pantalla será:

  F() de A

  F() de B

    Pese a todo lo comentado, hay que resaltar que la principal utilidad de poder indicar explícitamente si se desea redefinir u ocultar cada miembro es que facilita enormemente la resolución de problemas de versionado de tipos que puedan surgir si al derivar una nueva clase de otra y añadirle miembros adicionales, posteriormente se la desea actualizar con una nueva versión de su clase padre pero ésta contiene miembros que entran en conflictos con los añadidos previamente a la clase hija cuando aún no existían en la clase padre. En lenguajes donde implícitamente todos los miembros son virtuales, como Java, esto da lugar a problemas muy graves debidos sobre todo a:

  • Que por sus nombres los nuevos miembros de la clase padre entre en conflictos con los añadidos a la clase hija cuando no existían. Por ejemplo, si la versión inicial  de de la clase padre no contiene ningún método de nombre F(), a la clase hija se le añade void F() y luego en la nueva versión de la clase padre se incorporado int F(), se producirá un error por tenerse en la clase hija dos métodos F()

En Java para resolver este problema una posibilidad sería pedir al creador de la clase padre que cambiase el nombre o parámetros de su método, lo cual no es siempre posible ni conveniente en tanto que ello podría trasladar el problema a que hubiesen derivado de dicha clase antes de volverla a modificar. Otra posibilidad sería modificar el nombre o parámetros del método en la clase hija, lo que nuevamente puede llevar a incompatibilidades si también se hubiese derivado de dicha clase hija.

  • Que los nuevos miembros tengan los mismos nombres y tipos de parámetros que los incluidos en las clases hijas y sea obligatorio que toda redefinición que se haga de ellos siga un cierto esquema.

Esto es muy problemático en lenguajes como Java donde toda definición de método con igual nombre y parámetros que alguno de su clase padre es considerado implícitamente redefinición de éste, ya que difícilmente en una clase hija escrita con anterioridad a la nueva versión de la clase padre se habrá seguido el esquema necesario. Por ello, para resolverlo habrá que actualizar la clase hija para que lo siga y de tal manera que los cambios que se le hagan no afecten a sus subclases, lo que ello puede ser más o menos difícil según las características del esquema a seguir.

Otra posibilidad sería sellar el método en la clase hija, pero ello recorta la capacidad de reutilización de dicha clase y sólo tiene sentido si no fue redefinido en ninguna subclase suya.

En C# todos estos problemas son de fácil solución ya que pueden resolverse con sólo ocultar los nuevos miembros en la clase hija y seguir trabajando como si no existiesen.


Inicio | Tutorial C# PolimorfismoTutorial C#Miembros de tipo Versión para imprimir Foros de consulta


Añadir comentario ... Para preguntar utiliza los foros
Autor:

Título:


Para preguntar utiliza los foros.



Inicio | Tutorial C# PolimorfismoTutorial C#Miembros de tipo Versión para imprimir

Ocultación de miembros
Autor: José Antonio González Seco
Visitas: 9694 Fecha de publicación: 03/10/2006
José Antonio es experto en tecnologias Microsoft. Imparte cursos y conferencias en congresos sobre C# y .NET en Universidades de toda España (Sevilla, Barcelona, San Sebastián, Valencia, Oviedo, etc.) en representación de grandes empresas como Microsoft.




Visitas: 303 | Comentarios: 0 | Archivo: Articulos
Categorias: ASP.NET|CSS|ASP.NET
Visitas: 137 | Comentarios: 0 | Archivo: Articulos
Categorias: TFS
Visitas: 101 | Comentarios: 5 | Archivo: Articulos
Categorias: TFS
Visitas: 429 | Comentarios: 4 | Archivo: Articulos
Categorias: Transact-SQL|LinQ
Visitas: 1448 | Comentarios: 6 | Archivo: Articulos
Categorias: Visual Basic .NET|C#|ASP.NET|ASP.NET|Programación|ADO.NET
27/09/2008 Navegando ....
Visitas: 238 | Comentarios: 0 | Archivo: Articulos
Categorias: Humor
Visitas: 611 | Comentarios: 0 | Archivo: Articulos
Categorias: Visual Basic .NET|C#|ASP.NET|ASP.NET|Programación|WCF
Visitas: 2573 | Comentarios: 4 | Archivo: Articulos
Categorias: C#
Visitas: 463 | Comentarios: 0 | Archivo: Articulos
Categorias: JavaScript|ASP.NET
Visitas: 1072 | Comentarios: 0 | Archivo: Articulos
Categorias: Windows XP|Linux|Windows Vista

Útimos temas recibidos en los foros ...
Crystal reports XI Release 2 y asp.net por mvargas ... [ASP.NET] 0 21/11/2008
Enviar un email con C# utilizando .Net FrameWork 2.0 con logo... por Moises ... [ASP.NET] 0 21/11/2008
Enviar un email con C# utilizando .Net FrameWork 2.0 por Moises ... [C#] 0 21/11/2008
Cuestión de BFILE. por Megatron ... [ORACLE] 0 21/11/2008
Enviar email por Dol ... [ASP.NET] 5 21/08/2007
*******PREGUNTA********: por T.S.U.En Informatíca ... [Visual Basic 6.0] 5 17/03/2008
Programar tareas en oracle. por Rodrigo ... [ORACLE] 0 21/11/2008
fechas con datatimepicker por peyin ... [Visual Basic .NET] 3 20/11/2008
agregar datos de un gridview a un textbox por jhors ... [ASP.NET] 3 19/09/2007
Select de sql en c# por Stuart ... [C#] 1 20/11/2008
Manual en Español de SQL Navigator for Oracle por Maira ... [ORACLE] 12 30/03/2008
Como mandar mensaje a Celular por Pako ... [C#] 40 13/03/2007

Access CGI JSP ORACLE UNIX
Actualidad HTML/DHTML/XHTML LINUX PHP Visual Basic .NET
ASP ISAPI MS DOS Power Builder Visual Basic 6.0
ASP.NET Java mySQL SQL WIN 98/NT/2000/XP
C# JavaScript Opinion SQL Server

devjoker  Te recomendamos además ...
28/08/2006 Ubuntu en VmWare con VMware Tools
26/07/2006 Secuencias    forma parte de...Tutorial PL/SQL
22/10/2005 Insertar datos. INSERT    forma parte de...Tutorial SQL
03/10/2006 Programacion con C#    forma parte de...Tutorial C#
18/10/2005 Enviar emails a través de un servidor SMTP autenticado con Java
01/04/2006 Estructuras de control en PL/SQL    forma parte de...Tutorial PL/SQL
22/10/2005 Tablas    forma parte de...Tutorial SQL
11/09/2006 Trabajar con datos de tipo BLOB en ORACLE
21/07/2006 Funciones integradas de PL/SQL    forma parte de...Tutorial PL/SQL
22/10/2005 Actualización de datos. UPDATE    forma parte de...Tutorial SQL

 

Encuesta
¿A que perfil te adaptas mejor?
[Ver] [Votar]