2010-08-06 10 views
5

Questa domanda mi è venuta in mente mentre mi sto leggendo il palo Why doesn't C# support multiple inheritance? da MSDN Blog.multipla problema di successione in C#

Al primo sguardo il seguente codice:

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. Quindi è la classe base di A e B entrambi nel mio programma e anche fare A come classe base di B. Quindi, B ha ora due classi base, una è A e un'altra è Object. Eseguo l'override di un metodo GetHashCode() di Object Class nella classe A e B entrambi. Tuttavia, nella classe B, il metodo base.GetHashCode() restituisce il valore restituito del metodo GetHashCode() della classe A. Ma voglio questo valore da Object class. Come posso averlo?

+0

io dubito molto che si può fare questo. Qualche possibilità di espandere il ragionamento sul perché vorresti farlo? Lascerò che le menti più sagge ti diano una risposta più definitiva. So che non puoi farlo solo con il codice C#, ma forse è possibile attraverso la riflessione. –

+12

Si noti inoltre che "ereditarietà multipla" e il problema sono due problemi ortogonali, completamente non correlati. L'ereditarietà multipla potrebbe essere per te dichiarare una classe C che eredita sia da A che da B. –

+0

Credo che Lasse sia corretta. Questo non è possibile, certamente non senza riflessione. La domanda è: perché vorresti farlo in primo luogo? – Noldorin

risposta

2

No Classe B ha solo una classe base, classe A.

Per capire, classe A in realtà dovrebbe essere scritto come:

class A : Object 

Ma dal momento che è necessario, può essere lasciato fuori.

+1

Immagino che intendessi: ... poiché è ** non ** richiesto. – Timwi

+2

@Timwi Penso che intendesse che è necessario essere una classe figlio di Object, non che sia necessario definirlo esplicitamente. –

+0

Ah, capisco - interessante come entrambi i modi abbiano senso :) – Timwi

4

È possibile scrivere un metodo di istanza protetto in classe A che espone GetHashCode() da Object. Quindi chiamare il metodo protetto nella classe B.

0

Ottenere un hash della classe Object di base è irrilevante ai fini del metodo. L'intero punto di GetHashCode() consiste nel generare un valore di identità univoco (il più possibile) in base al tipo e allo stato specifici dell'oggetto.

Il metodo GetHashCode() utilizza un algoritmo di hashing predefinito che può essere utilizzato per determinare l'identità dell'istanza e l'univocità, sebbene l'univocità non sia garantita. L'algoritmo predefinito utilizza lo stato dell'oggetto per generare l'hash.

Maggiori informazioni: http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

0

temo si stia mescolando eredità normale con l'ereditarietà multipla. Multiple inheritance significa che hai 3 classi A è sottoclasse di B e C ma né B è sottoclasse di C né C è sottoclasse di B.

L'ereditarietà normale consente il concatenamento come A è una sottoclasse di B che è sottoclasse di C. Se chiami metodo, puoi scorrere le classi e controllare quale metodo chiamare (in realtà non controlla le classi una per una). Qui B è una sottoclasse di A che è sottoclasse di Object. L'oggetto sembra avere il metodo (virtuale) GetHashCode - sovraccaricandolo nella sottoclasse.Il metodo Object è nascosto e (senza trucchi) non può essere utilizzato.

0

Si dovrebbe considerare un'alternativa alla chiamata Object.GetHashCode(). Ecco perché (from MSDN Library):

L'implementazione predefinita del metodo GetHashCode non garantisce valori di ritorno uniche per diversi oggetti. Inoltre, il Framework .NET non garantisce l'attuazione predefinita del metodo GetHashCode , e il valore che ritorna sarà lo stesso tra versioni diverse di .NET Framework . Di conseguenza, l'implementazione predefinita di questo metodo con non deve essere da utilizzare come identificatore di oggetto univoco per scopi di hashing.

+0

Quel paragrafo dice semplicemente, "' GetHashCode' restituisce un codice hash ". I codici hash non devono essere unici; Test di "pari" per l'unicità. 'GetHashCode' potrebbe restituire' 0' per tutti gli oggetti, a patto che 'Equals' restituisca correttamente' false' quando viene assegnato un oggetto diverso. –

1

Sì, questo non è correlato all'eredità multipla (incidentalmente, mi manca l'ereditarietà privata più dell'eredità multipla). Questa domanda è anche una cosa abbastanza orribile a cui pensare.

Se si ha il controllo di classe A, allora bene:

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(); 
    } 
} 

Se non lo fai:

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, vorrei mettere quel codice nella categoria dei "non dobbiamo mai parlare di nuovo ". Presumo che questo si riferisca ad un principio più generale, dato che qui gli hashcode sono comunque inutili, ma non sono sicuro di quale sia il principio più generale. Certo, a volte può essere fastidioso quando qualcosa è nascosto dalle classi che vogliamo ottenere, ma può essere molto peggio quando accade il contrario.

Indietro ai tempi di VB6, i codificatori in quel linguaggio dovevano spesso fare cose piuttosto sgradevoli coinvolgendo l'indirizzo di memoria del v-table usato per la ricerca di chiamate virtuali, alterando la protezione della memoria e quindi modificando i byte memorizzati lì a mano, perché in "proteggere" gli utenti dai dettagli di implementazione hanno tenuto alcune cose vitali lontane da loro (non si potevano creare enumeratori senza questa tecnica, si passavano solo quelli per altri oggetti).

Grazie a Dio non siamo "protetti" in questa misura in C#. Se dovessi scrivere codice come la risposta che ho dato sopra per un vero progetto, avrò bisogno di un paio di drink per recuperare più tardi quella sera.