2009-01-07 8 views
23

Ho una sottoclasse con un metodo over-ridden che so sempre restituisce un sottotipo particolare del tipo restituito dichiarato nella classe base. Se scrivo il codice in questo modo, non verrà compilato. Dal momento che, probabilmente, non ha senso, mi permetta di dare un esempio di codice:Come restituire il sottotipo nel metodo sovrascritto della sottoclasse in C#?

class BaseReturnType { } 
class DerivedReturnType : BaseReturnType { } 

abstract class BaseClass { 
    public abstract BaseReturnType PolymorphicMethod(); 
} 

class DerivedClass : BaseClass { 
    // Compile Error: return type must be 'BaseReturnType' to match 
    // overridden member 'BaseClass.PolymorphicMethod()' 
    public override DerivedReturnType PolymorphicMethod() { 
     return new DerivedReturnType(); 
    } 
} 

Esiste un modo per raggiungere questo obiettivo in C#? In caso contrario, qual è il modo migliore per ottenere qualcosa di simile? E perché non è permesso? Non sembra consentire alcuna incoerenza logica, dal momento che qualsiasi oggetto restituito dal metodo sovrascritto è ancora is BaseReturnType. Forse c'è qualcosa che non avevo considerato però. O forse la ragione è tecnologica o storica.

+0

Cosa dice il compilatore? –

+0

Ho incluso l'errore del compilatore come commento. – recursive

risposta

17

Sfortunatamente no, i tipi di ritorno covarianti non sono supportati in C# per l'override del metodo. (Ditto tipi di parametri controvarianti.)

Se si sta implementando un'interfaccia, è possibile implementarla esplicitamente con la versione "debole" e fornire anche una versione pubblica con il contratto più forte. Per semplice ignoranza di una classe genitore, non ho questo lusso ho paura :(

(MODIFICA: Marc ha un reasonable solution - anche se è piuttosto brutto, e nascondere il metodo è generalmente una cosa negativa per la leggibilità. offesa, Marc;.)

io credo questo è in realtà una restrizione CLR, non solo un linguaggio di uno - ma potrei anche sbagliarmi

(Come una questione di storia, Java (il linguaggio) ha avuto la stessa restrizione fino a 1,5 - ma ha acquisito covarianza contemporaneamente ai farmaci generici.)

+1

Nessuno preso ;-p Si noti, tuttavia, che questo è esattamente il modello utilizzato da molti BCL .NET, ad esempio DbConnection ha CreateCommand, che esegue lo shim tramite il comando "Protetto astratto" di CreateDbCommand. –

+1

Vedi questo commento di Eric Lippert sul perché C# non lo supporta http://stackoverflow.com/questions/1319663/why-c-doesnt-allow-inheritance-of-return-type-when-implementing-an-interface/1320710 # 1320710 –

0

Mi sembra che sia necessario restituire un'interfaccia, non una classe base.

+1

In primo luogo non è sempre applicabile, e in secondo luogo potrebbe non essere d'aiuto in ogni caso - potresti indicare che stai restituendo un'implementazione di ISpecificInterface dove la classe base dichiara che restituirà IGeneralInterface. –

1
class BaseReturnType { } 
class DerivedReturnType : BaseReturnType { } 

abstract class BaseClass { 
    public abstract BaseReturnType PolymorphicMethod(); 
} 

class DerivedClass : BaseClass { 
    // Error: return type must be 'BaseReturnType' to match 
    // overridden member 'BaseClass.PolymorphicMethod()' 
    public override BaseReturnType PolymorphicMethod() { 
     return new DerivedReturnType(); 
    } 
} 

questo dovrebbe funzionare

+0

Penso che l'OP voglia che il metodo sia * dichiarato * come DerivedReturnType, non solo per restituirlo. –

+0

So che questo è possibile, ma poi richiede di lanciare (nuovo DerivedClass()). PolymorphicMethod() se si vuole usarlo come DerivedReturnType. Potrebbe essere quello che finisco, ma non mi piace il casting se posso evitarlo. – recursive

+0

può eseguire il returnval ovunque sia necessario come Derivato – anand

12

È possibile farlo se si introduce un metodo in più per eseguire l'override (dal momento che non si può override e new un metodo con lo stesso nome nello stesso tipo):

abstract class BaseClass 
{ 
    public BaseReturnType PolymorphicMethod() 
    { return PolymorphicMethodCore();} 

    protected abstract BaseReturnType PolymorphicMethodCore(); 
} 

class DerivedClass : BaseClass 
{ 
    protected override BaseReturnType PolymorphicMethodCore() 
    { return PolymorphicMethod(); } 

    public new DerivedReturnType PolymorphicMethod() 
    { return new DerivedReturnType(); } 
} 

Ora si dispone di un metodo PolymorphicMethod a ogni livello con il tipo corretto.

16

Si potrebbe fare la classe generica, se questo non vi dà fastidio:

class BaseReturnType { } 
    class DerivedReturnType : BaseReturnType { } 

    abstract class BaseClass<T> where T : BaseReturnType 
    { 
     public abstract T PolymorphicMethod(); 
    } 

    class DerivedClass : BaseClass<DerivedReturnType> 
    { 
     // Error: return type must be 'BaseReturnType' to match 
     // overridden member 'BaseClass.PolymorphicMethod()' 
     public override DerivedReturnType PolymorphicMethod() 
     { 
      return new DerivedReturnType(); 
     } 
    } 
1

cambiare il metodo di firma su classe derivata per:

public override BaseReturnType PolymorphicMethod() 
{ 
    return new DerivedReturnType();  
} 

C# non supporta i tipi di ritorno variante. È possibile controllare questo post di un modo per farlo usando Generics ... http://srtsolutions.com/blogs/billwagner/archive/2005/06/17/covaraint-return-types-in-c.aspx

Ecco un esempio utilizzando Generics nel modello:

public class BaseReturnType 
{ 
} 
public class DerivedReturnType : BaseReturnType 
{ 
} 

public abstract class BaseClass<T> where T : BaseReturnType 
{ 
    public abstract T PolymorphicMethod(); 

} 

public class DerviedClass : BaseClass<DerivedReturnType> 
{ 
    public override DerivedReturnType PolymorphicMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 
2

Generics non sono necessariamente la strada da percorrere. In particolare, il tipo (di Derived) non è considerato un tipo (di Base).

Innanzitutto, aggiungere un nuovo metodo alla classe derivata che restituirà il valore con il tipo corretto.In secondo luogo, contrassegna il metodo di override non sovrascrivibile e assegnalo al nuovo metodo.

Questo è tutto. Hai risolto il tuo problema. Le classi figlie non saranno in grado di ri-espandere il tipo perché devono sostituire il nuovo metodo.

Mi scuso se il codice non è corretto; Sono abituato a VB.net.

abstract class C1 { 
    public abstract IEnumerable<Byte> F1(); 
} 
class C2 : C1 { 
    public sealed override IEnumerable<Byte> F1() { 
     Return F2(); 
    } 
    public overridable IList<Byte> F2() { 
     Return {1, 2, 3, 4}; 
    } 
} 
Problemi correlati