2010-08-06 24 views
5

Esta pregunta me vino a la mente mientras estoy leyendo el puesto Why doesn't C# support multiple inheritance? de MSDN Blog.problema de herencia múltiple en C#

Al primer vistazo a la siguiente código:

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class A 
    { 
     int num; 
     public A() 
     { 
      num = 0; 
     } 
     public A(int x) 
     { 
      num = x; 
     } 
     public override int GetHashCode() 
     { 
      return num + base.GetHashCode(); 
     } 
    } 
    class B : A 
    { 
     int num; 
     public B() 
     { 
      num = 0; 
     } 
     public B(int x) 
     { 
      num = x; 
     } 
     public override int GetHashCode() 
     { 
      return num + base.GetHashCode(); 
     } 
    } 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      A a = new A(); 
      B b = new B(); 
      Console.Write(a.GetHashCode() + " " + b.GetHashCode()); 
      Console.Read(); 
     } 
    } 
} 

Object class is the ultimate base class of all classes. Por lo tanto, es la clase base de A y B tanto en mi programa y también hacer A como una clase base de B. Entonces, B tiene ahora dos clases base, una es A y otra es Object. Anulo un método GetHashCode() de Object Class en la clase A y B ambos. Pero en la clase B, el método base.GetHashCode() devuelve el valor de retorno del método GetHashCode() de la clase A. Pero quiero este valor desde Object class. ¿Cómo puedo obtener eso?

+0

Dudo mucho que usted puede hacer esto. ¿Alguna posibilidad de ampliar el razonamiento detrás de por qué querrías hacer eso? Sin embargo, dejaré que las mentes más inteligentes te den una respuesta más definitiva. Sé que no puedes hacerlo solo con el código C#, pero tal vez sea posible a través de la reflexión. –

+12

También tenga en cuenta que "herencia múltiple" y su problema son dos problemas ortogonales, sin relación alguna. La herencia múltiple sería para que usted declare una clase C que hereda de A y B. –

+0

Creo que Lasse es correcto. Esto no es posible, ciertamente no sin reflexión. Sin embargo, la pregunta es por qué querrías hacer esto en primer lugar. – Noldorin

Respuesta

2

No Clase B sólo tiene una clase base, la clase A.

Para entender, clase A en realidad debería ser escrita como:

class A : Object 

Pero ya que lo que se requiere, se puede dejar fuera.

+1

Supongo que quería decir: ... ya que ** no ** es obligatorio. – Timwi

+2

@Timwi Creo que quiso decir que se requiere que sea una clase hija de Object, no que se requiera que defina explícitamente eso. –

+0

Ah, ya veo - interesante cómo ambos sentidos tienen sentido :) – Timwi

4

Puede escribir un método de instancia protegida en la clase A que expone GetHashCode() desde Object. Luego llame a ese método protegido en la clase B.

0

Obtener un hash de la clase base Object es irrelevante para el propósito del método. El objetivo de GetHashCode() es generar un valor de identidad único (como sea posible) en función del tipo específico y el estado del objeto.

El método GetHashCode() utiliza un algoritmo hash predeterminado que se puede utilizar para determinar la identidad y singularidad de la instancia, aunque la exclusividad no está garantizada. El algoritmo predeterminado usa el estado del objeto para generar el hash.

Más información: http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

0

Me temo que usted está mezclando la herencia normal con la herencia múltiple. Múltiple herencia y que tiene 3 clases A es la subclase de B y C pero ni B es la subclase de C ni C es la subclase de B.

La herencia normal permite encadenar como A es la subclase de B que es la subclase de C. Si llama al método, puede recorrer las clases y verificar qué método utilizar (en realidad, no verifica las clases una a una). Aquí B es la subclase de A que es la subclase de Objeto. El objeto tiene el método (virtual) GetHashCode - al sobrecargarlo en la subclase.El método Object está oculto y (sin trucos) no se puede usar.

0

Debería considerar una alternativa a llamar a Object.GetHashCode(). He aquí por qué (from MSDN Library):

La implementación predeterminada del método GetHashCode no garantiza valores de retorno únicas para diferentes objetos. Además, .NET Framework no garantiza la aplicación por defecto del método GetHashCode , y el valor que rendimientos será la misma entre versiones diferentes de la .NET Framework. Por lo tanto, la implementación predeterminada de de este método no debe ser como identificador de objeto único con fines de hash.

+0

Ese párrafo solo dice, "' GetHashCode' devuelve un código hash ". Los códigos Hash no tienen que ser únicos; 'Igual' prueba para unicidad. 'GetHashCode' podría devolver' 0' para todos los objetos, siempre que 'Equals' devuelva' false' correctamente cuando se le den diferentes objetos. –

1

Sí, esto no se relaciona con la herencia múltiple en absoluto (por cierto, extraño la herencia privada más que la herencia múltiple). Esta pregunta también es bastante horrible de pensar.

Si usted tiene el control de la clase A, entonces está bien:

class A 
{ 
    /*elide the stuff you already have*/ 
    protected int GetBaseHashCode() 
    { 
     return base.GetHashCode(); 
    } 
} 
class A : B 
{ 
    /*elide stuff already in example*/ 
    public override int GetHashCode() 
    { 
     return num + GetBaseHashCode(); 
    } 
} 

Si no lo hace:

public class B : A 
{ 
    private static readonly DynamicMethod GRANDPARENT_GET_HASH_CODE; 
    static B() 
    { 
     MethodInfo gpGHC = typeof(object).GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Instance); 

     GRANDPARENT_GET_HASH_CODE = new DynamicMethod("grandParentGHC", typeof(int), new Type[] { typeof(object) }, typeof(object)); 
     ILGenerator il = GRANDPARENT_GET_HASH_CODE.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg, 0); 
     il.EmitCall(OpCodes.Call, gpGHC, null); 
     il.Emit(OpCodes.Ret); 
    } 
    private int num; 
    public B() 
    { 
     num = 0; 
    } 
    public B(int x) 
    { 
     num = x; 
    } 
    public override int GetHashCode() 
    { 
     return num + (int)GRANDPARENT_GET_HASH_CODE.Invoke(null, new object[]{this}); 
    } 
} 

Francamente, me gustaría poner ese código en la categoría de "nunca debemos hablar de nuevo ". Supongo que esto se relaciona con un principio más general, ya que los códigos de hash aquí son inútiles de todos modos, pero no estoy seguro de cuál es ese principio más general. Ciertamente, a veces puede ser molesto cuando algo se oculta de las clases que queremos alcanzar, pero puede ser mucho peor cuando sucede lo contrario.

En los tiempos de VB6, los programadores en ese lenguaje a menudo tenían que hacer cosas bastante desagradables al obtener la dirección de memoria de la tabla v utilizada para la búsqueda de llamadas virtuales, alterando la protección de la memoria y ajustando los bytes almacenados allí a mano, porque al "proteger" a los usuarios de los detalles de implementación, les ocultaba algunas cosas vitales (no podía crear enumeradores sin esta técnica, solo transferirlos para otros objetos).

Gracias a Dios que no estamos "protegidos" en esa medida en C#. Si alguna vez tengo que escribir un código como la respuesta que di más arriba para un proyecto real, voy a necesitar algunos tragos para recuperarlos más tarde esa noche.