Posso eseguire l'override con tipi derivati?
-
03-07-2019 - |
Domanda
Per quanto ne so non è possibile effettuare le seguenti operazioni in C # 2.0
public class Father
{
public virtual Father SomePropertyName
{
get
{
return this;
}
}
}
public class Child : Father
{
public override Child SomePropertyName
{
get
{
return this;
}
}
}
Ho risolto il problema creando la proprietà nella classe derivata come " new " ;, ma ovviamente questo non è polimorfico.
public new Child SomePropertyName
C'è qualche soluzione in 2.0? Che dire delle funzionalità in 3.5 che affrontano questa questione?
Soluzione
Questo non è possibile in nessun linguaggio .NET a causa di problemi di sicurezza del tipo. Nei linguaggi sicuri per i tipi, è necessario fornire covarianza per i valori di ritorno e contravarianza per i parametri. Prendi questo codice:
class B {
S Get();
Set(S);
}
class D : B {
T Get();
Set(T);
}
Per i metodi Get
, covarianza significa che T
deve essere S
o un tipo derivato da D
. Altrimenti, se avessi un riferimento a un oggetto di tipo B
memorizzato in una variabile digitata B.Get()
, quando avessi chiamato Set
non avresti ottenuto un oggetto rappresentabile come B.Set(X)
back - rompendo il sistema di tipi .
Per i metodi X
, la contraddizione significa che D::Set(T)
deve essere <=> o un tipo da cui <=> deriva. Altrimenti, se avevi un riferimento a un oggetto di tipo <=> memorizzato in una variabile digitata <=>, quando hai chiamato <=>, dove <=> era di tipo <=> ma non di tipo <=>, < => otterrebbe un oggetto di un tipo che non si aspettava.
In C #, c'è stata una decisione consapevole di non consentire la modifica del tipo quando si sovraccaricavano le proprietà, anche quando avevano solo una coppia getter / setter, perché altrimenti avrebbe un comportamento molto incoerente ( " Vuoi dire, posso cambiare il tipo su quello con un getter, ma non uno sia con un getter che con un setter? Perché no?!? & Quot; - Anonimo Alternativo Newbie).
Altri suggerimenti
Puoi ri-dichiarare (nuovo), ma non puoi ri-dichiarare e sovrascrivere allo stesso tempo (con lo stesso nome). Un'opzione è utilizzare un metodo protetto per nascondere i dettagli: ciò consente sia il polimorfismo che il nascondersi allo stesso tempo:
public class Father
{
public Father SomePropertyName
{
get {
return SomePropertyImpl();
}
}
protected virtual Father SomePropertyImpl()
{
// base-class version
}
}
public class Child : Father
{
public new Child SomePropertyName
{
get
{ // since we know our local SomePropertyImpl actually returns a Child
return (Child)SomePropertyImpl();
}
}
protected override Father SomePropertyImpl()
{
// do something different, might return a Child
// but typed as Father for the return
}
}
No, ma puoi utilizzare generics in 2 e versioni successive:
public class MyClass<T> where T: Person
{
public virtual T SomePropertyName
{
get
{
return ...;
}
}
}
Quindi Father and Child sono versioni generiche della stessa classe
Da Wikipedia :
Nel linguaggio di programmazione C #, supporto per entrambi i tipi di ritorno covarianza e parametro è stata aggiunta la contraddizione per i delegati nella versione 2.0 della lingua. Né covarianza né contraddizione sono supportati per l'override del metodo.
Tuttavia non dice esplicitamente nulla sulla covarianza delle proprietà.
Puoi creare un'interfaccia comune per padre e figlio e restituire un tipo di tale interfaccia.
No. C # non supporta questa idea (si chiama & Quot; return type covariance & Quot;). Puoi comunque farlo:
public class FatherProp
{
}
public class ChildProp: FatherProp
{
}
public class Father
{
public virtual FatherProp SomePropertyName
{
get
{
return new FatherProp();
}
}
}
public class Child : Father
{
public override FatherProp SomePropertyName
{
get
{
// override to return a derived type instead
return new ChildProp();
}
}
}
vale a dire. usa il contratto definito dalla classe base, ma restituisce un tipo derivato. Ho fatto un esempio più dettagliato per chiarire questo punto: restituendo & Quot; questo & Quot; di nuovo non cambierebbe nulla.
È possibile (ma disordinato) testare l'oggetto restituito per il suo tipo effettivo (cioè " se someObject è ChildProp "), ma è meglio chiamare un metodo virtuale su di esso che lo fa la cosa giusta per il suo tipo.
Il metodo virtuale della classe base (in questo caso, proprietà virtuale) non solo ha un'implementazione, ma definisce anche un contratto: che una classe figlio può fornire un'implementazione diversa di SomePropertyName se soddisfa questo contratto (ovvero SomePropertyName restituisce un oggetto di tipo " FatherProp "). Restituzione di un oggetto di tipo & Quot; ChildProp & Quot; derivato da " FatherProp " soddisfa questo contratto. Ma non puoi modificare il contratto tra & Quot; Child & Quot; - questo contratto si applica a tutte le classi discendenti da " Father " ;.
Se fai un passo indietro e osservi il tuo design più ampio, ci sono altri costrutti di linguaggio nel toolkit C # a cui potresti anche voler pensare: Generics o interfacce.
No. C # non supporta questa idea (si chiama " tipo di ritorno covarianza quot &;).
Da Wikipedia:
Nel linguaggio di programmazione C #, supporto per entrambi i tipi di ritorno covarianza e parametro è stata aggiunta la contraddizione per i delegati nella versione 2.0 della lingua. Né covarianza né contraddizione sono supportati per l'override del metodo.
Puoi ri-dichiarare (nuovo), ma tu non è possibile ri-dichiarare e sovrascrivere in stesso tempo (con lo stesso nome). Uno l'opzione è utilizzare un metodo protetto per nascondi i dettagli - questo consente entrambi polimorfismo e nascondersi allo stesso tempo:
Le migliori soluzioni sarebbero usare generici:
public class MyClass<T> where T: Person
{
public virtual T SomePropertyNameA
{
get { return ...; }
}
}//Then the Father and Child are generic versions of the same class
Questo è il più vicino che potrei venire (finora):
public sealed class JustFather : Father<JustFather> {}
public class Father<T> where T : Father<T>
{ public virtual T SomePropertyName
{ get { return (T) this; }
}
}
public class Child : Father<Child>
{ public override Child SomePropertyName
{ get { return this; }
}
}
Senza la classe JustFather
, non è possibile creare un'istanza di Father<T>
se non fosse un altro tipo derivato.