2010-08-23 7 views
10

Sto provando a sovrascrivere una proprietà in una classe base con un tipo diverso, ma derivato con lo stesso nome. Penso che sia possibile per covarienza o generici ma non sono sicuro di come farlo?Ignora una proprietà con un tipo derivato e lo stesso nome C#

Il codice seguente ottiene l'errore:

Error 1 'Sun.Cache': type must be 'OuterSpace.Cache' to match overridden member 'OuterSpace.Cache'

public class OuterSpace { 
    public virtual OuterSpaceData Data {get; set;} 
    public virtual OuterSpaceAnalysis Analysis {get; set;} 
    public virtual OuterSpaceCache Cache {get; set;} 


    public class OuterSpaceData { 
     //Lots of basic Data Extraction routines eg 
     public virtual GetData(); 
    } 
    public class OuterSpaceAnalysis { 
     //Lots of Generic Analysis on Data routines eg 
     public virtual GetMean(); 
    } 
    public class OuterSpaceCache { 
     //Lots of Caches of Past Analysis Results: 
     public Dictionary<AnalysisType, List<Result>> ResultCache; 
    } 
} 

public class Sun : OuterSpace { 
    public override SunData Data {get; set;} 
    public override SunAnalysis Analysis {get; set;} 
    public override SunCache Cache {get; set;} 

    public SunData : OuterSpaceData { 
     //Routines to specific get data from the sun eg 
     public override GetData(); 
    } 

    public SunAnalysis : OuterSpaceAnalysis { 
     //Routines specific to analyse the sun: eg 
     public double ReadTemperature(); 
    } 
    public SunCache : OuterSpaceCache { 
     //Any data cache's specific to Sun's Analysis 
     public Dictionary<AnalysisType, List<Result>> TempCache; 
    } 
} 

public class Moon : OuterSpace {} etc. 

Per il risultato finale, quando mi rivolgo l'oggetto "dati" di Sun non voglio che ci sia due Data Objects (ereditato & Base Class) ma quando provo a sovrascrivere la proprietà richiede che le variabili Sun siano dello stesso tipo della classe base. Ad esempio:

Sun EarthSun = new Sun() 
EarthSun.Analyse() //The OuterSpace Analysis Saves results to Sun Cache: 

//Now try use the result: 
EarthSun.Cache[0]... 

Molto simile a questo, ma con tipo derivato, invece di string-array: C# Member variable overrides used by base class method

E questa risposta non ha molto senso per me: How to override member of base class after inheritance in C++

O forse questo significa che non è possibile? Can I Override with derived types?

Help! :) Qualche lavoro in giro?

+1

Un po 'fuori tema, ma ha senso dire che Sun è una specie di OuterSpace? Alcune delle difficoltà in questo caso potrebbero derivare dall'uso dell'ereditarietà laddove sarebbe sufficiente una struttura più semplice. –

+0

Accolgo suggerimenti? Questo è un pseudo codice demo del problema, il vero progetto è molto grande e sto solo cercando di pianificare il futuro. – JaredBroad

+0

è difficile da dire solo dalla struttura campione che hai fornito. La cosa principale da considerare è come stai effettivamente _using_ le classi. In particolare, cosa si tratta delle classi Cache, Analysis e Data che rendono vantaggioso accedervi come classi base? Hai una classe che coordina l'analisi, ma sembra anche che il coordinamento sia diverso per ogni tipo di analisi; questo mi suggerisce che sono responsabilità nascoste che potrebbero richiedere ulteriori indagini. Se non si dispone ancora di un caso d'uso per la struttura generica, lo eviterei; YAGNI. –

risposta

11

Non è possibile il modo in cui lo si desidera, esattamente.

È possibile creare una "nuova" proprietà con lo stesso nome:

public new SunData Data 
{ 
    get { return (SunData) base.Data; } 
    set { base.Data = value; } 
} 

non è proprio la stessa cosa, ma è probabilmente il più vicino si sta andando ad ottenere.

Un altro approccio possibile sarebbe:

public SunData SunData { get; set; } 

public override OuterSpaceData Data 
{ 
    get { return SunData; } 
    set { SunData = (SunData)value; } 
} 

Il vantaggio di questo approccio è che non garantisce che è impossibile mettere qualcosa nella vostra proprietà I dati che non è un SunData. Lo svantaggio è che la proprietà Data non è fortemente tipizzata e devi usare la proprietà SunData se vuoi che venga tipizzata staticamente su SunData.

C'è un modo brutto per ottenere il "meglio di entrambi i mondi" a scapito di essere confuso per il codice e difficile da comprendere in seguito - introducendo una classe derivata intermedia che esegue un 'override sealed' sulla classe base. e reindirizza a una diversa proprietà protetta con un nuovo nome, quindi avere la classe derivata definitiva aggiungere una 'nuova' proprietà Data che quindi chiama la proprietà intermedia.È davvero un brutto scherzo, però, e anche se l'ho fatto, non lo consiglierei.

+1

Come probabilmente già sapete, il "nuovo" approccio alla proprietà può avere effetti collaterali indesiderati quando l'oggetto viene lanciato su una classe base. – kbrimington

+1

Grazie, non riesco a vedere: questo risolve il problema del fatto che SunCache è Null perché Analysis sta salvando i risultati nella classe Base? Che dire delle variabili extra in SunCache? Saranno persi? – JaredBroad

+0

Inoltre, si noti che nel secondo approccio, i dati sono fortemente tipizzati; ci manca semplicemente il controllo del tipo in fase di compilazione per le chiamate alla proprietà. – kbrimington

1

Se stai cercando il percorso covarianza, qualcosa come questo dovrebbe funzionare:

public class OuterSpace<TCacheType> where TCacheType : OuterSpaceCache { 
    public virtual OuterSpaceData Data {get; set;} 
    public virtual OuterSpaceAnalysis Analysis {get; set;} 
    public virtual TCacheType Cache {get; set;} 


    public class OuterSpaceData { 
     //Lots of basic Data Extraction routines eg 
     public virtual GetData(); 
    } 
    public class OuterSpaceAnalysis { 
     //Lots of Generic Analysis on Data routines eg 
     public virtual GetMean(); 
    } 
    public class OuterSpaceCache { 
     //Lots of Caches of Past Analysis Results: 
     public Dictionary<AnalysisType, List<Result>> ResultCache; 
    } 
} 

public class Sun : OuterSpace<SunCache> { 
    public override SunData Data {get; set;} 
    public override SunAnalysis Analysis {get; set;} 

    public SunData : OuterSpaceData { 
     //Routines to specific get data from the sun eg 
     public override GetData(); 
    } 

    public SunAnalysis : OuterSpaceAnalysis { 
     //Routines specific to analyse the sun: eg 
     public double ReadTemperature(); 
    } 
    public SunCache : OuterSpaceCache { 
     //Any data cache's specific to Sun's Analysis 
     public Dictionary<AnalysisType, List<Result>> TempCache; 
    } 
} 

Fair Warning, il codice non compilato completamente e ho potuto essere totalmente sbagliato :) Ma è una pugnalata a quello che ti penso vuole ...

Oh, e io penso questa è una unica soluzione di 4.0 .Net, non credo che si può fare questo in precedenza, co-varianza è stato introdotto lì ...

0

Se SunData è un compito compatibile con Outer spazio Dati (ereditato da) quindi non è necessario modificare il tipo di di OuterSpaceData

+0

Ho bisogno di accedere a variabili/metodi specifici SunCache/SunAnalysis (preferibilmente senza dover eseguire il cast ogni volta che ho bisogno di usarli). Questo non significa che il tipo deve essere impostato correttamente? – JaredBroad

+0

@JaredBroad quello che stai dicendo è che hai un odore di design. Sei sicuro che "mai" (come in molto molto raramente) devono sottostare. Sembra che tu stia derivando per la funzionalità dai un'occhiata al modello di strategia e metti la tua logica/funzionalità comune in classi separate e inietti oggetti di questi tipi –

+0

Grazie per il suggerimento, puoi vedere anche le interfacce che aiutano, giocheranno più tardi quando ottieni il tempo. http://en.wikipedia.org/wiki/Strategy_pattern#C.23 – JaredBroad

0
Non

sicuro di Generics, ma si potrebbe ottenere un effetto limitato con semplici polimorfismo (supponendo SunData eredita da dati etc)

public class Sun : OuterSpace 
{ 
    void SomeInitializationMethod() 
    { 
    Data = new SunData(); 
    Analysis = new SunAnalysis(); // etc 
    } 

    } 

// You could also make casting simpler with a generic method on the base 
public T GetData<T>() 
{ 
    if (Data is T) 
    { 
    return Data as T; 
    } 
    return null; 
}; 
+0

Grazie, inizialmente aveva qualcosa di simile ma voleva evitare i cast disseminati attraverso il codice laddove possibile. Almeno con la risposta selezionata, il casting è centralizzato ..: \ – JaredBroad

7

provare ad andare su di esso con i generici. La seguente classe fornisce una classe base generica per OuterSpace con vincoli sui suoi parametri che applicano regole di ereditarietà sui tipi di proprietà. Ho omesso i metodi nei piccoli tipi per chiarezza.

public class OuterSpace<TData, TAnalysis, TCache> 
    where TData : OuterSpaceData 
    where TAnalysis : OuterSpaceAnalysis 
    where TCache : OuterSpaceCache 
{ 
    public virtual TData Data { get; set; } 
    public virtual TAnalysis Analysis { get; set; } 
    public virtual TCache Cache { get; set; } 
} 
public class OuterSpaceData { } 
public class OuterSpaceAnalysis { } 
public class OuterSpaceCache { } 

public class Sun : OuterSpace<SunData, SunAnalysis, SunCache> 
{ 
    public override SunData Data { get; set; } 
    public override SunAnalysis Analysis { get; set; } 
    public override SunCache Cache { get; set; } 
} 
public class SunData : OuterSpaceData { } 
public class SunAnalysis : OuterSpaceAnalysis { } 
public class SunCache : OuterSpaceCache { } 

Il fascino di questo approccio è che permette di restituire le proprietà fortemente tipizzate e far rispettare un vincolo eredità di base sui tipi di quelle proprietà. I metodi sono completamente sovrascrivibili; tuttavia, si tenga presente che potrebbe essere necessario eseguire l'override per evitare problemi di trasmissione con l'implementazione della classe base.

+0

Questo sembra carino, e forse avrebbe funzionato, ma ho cercato di implementarlo e si è complicato molto velocemente. Il programma è in realtà lungo 50-100k e dovendo ridigitare ogni "OuterSpace" mi ha spaventato un po ':) Grazie però, – JaredBroad

+0

@ Jared: Grazie per la tua risposta. È carina e funziona; tuttavia, potresti trovare adeguato usare l'approccio di @ stuart (il 2 ° - evitare le dichiarazioni dei membri 'new' se possibile) e usare cast di tipo quando necessario per ottenere metodi specializzati. (ad esempio 'var data = mySunDataObject.Data as SunData;'). – kbrimington

+0

@ Jared: Nota anche che le tre sostituzioni nella classe 'Sun' sono davvero necessarie solo se si intende avere un comportamento diverso su getter e setter. Per i getter e setter vuoti normali come nell'esempio, è possibile rimuovere gli override e ottenere lo stesso effetto. – kbrimington