Domanda

Di recente ho riscontrato un problema in cui sembra che abbia bisogno di un metodo "statico astratto". So perché è impossibile, ma come posso aggirare questa limitazione?

Ad esempio ho una classe astratta che ha una stringa di descrizione. Poiché questa stringa è comune per tutte le istanze, è contrassegnata come statica, ma voglio richiedere che tutte le classi derivate da questa classe forniscano la propria proprietà Description, quindi l'ho contrassegnata come astratta:

abstract class AbstractBase
{
    ...
    public static abstract string Description{get;}
    ...
}

Ovviamente non verrà compilato. Ho pensato di utilizzare le interfacce ma le interfacce potrebbero non contenere firme di metodi statici.

Devo renderlo semplicemente non statico e ottenere sempre un'istanza per ottenere informazioni specifiche su quella classe?

Qualche idea?

È stato utile?

Soluzione

La combinazione di statico e astratto è in qualche modo insignificante, sì. L'idea alla base di static è che non è necessario presentare un'istanza della classe per utilizzare il membro in questione; tuttavia, con abstract, ci si aspetta che un'istanza sia di una classe derivata che fornisce un'implementazione concreta.

Posso capire perché vorresti questo tipo di combinazione, ma il fatto è che l'unico effetto sarebbe negare l'uso dell'implementazione di 'this' o di qualsiasi membro non statico. Cioè, la classe genitore imporrebbe una limitazione nell'implementazione della classe derivata, anche se non vi è alcuna differenza di fondo tra la chiamata di un membro astratto o "statico astratto" (poiché entrambi avrebbero bisogno di un'istanza concreta per capire quale implementazione usare)

Altri suggerimenti

Non puoi.

Il posto dove farlo è con gli attributi.

Esempio

[Name("FooClass")]
class Foo
{
}

Se non ti dispiace rimandare alle implementazioni per implementare sensibilmente la proprietà Description, puoi semplicemente farlo

public abstract string ClassDescription {get; } 
// ClassDescription is more intention-revealing than Description

E le classi di implementazione farebbero qualcosa del genere:

static string classDescription="My Description for this class";
override string  ClassDescription { get { return classDescription; } }

Quindi, le tue classi sono tenute a seguire il contratto di avere una descrizione, ma lo lasci a loro per farlo in modo sensato. Non c'è modo di specificare un'implementazione orientata agli oggetti (se non attraverso hack crudeli e fragili).

Tuttavia, nella mia mente questa descrizione è metadata di classe, quindi preferirei usare il meccanismo di attributo come altri hanno descritto. Se sei particolarmente preoccupato per molteplici usi della riflessione, crea un oggetto che rifletta sull'attributo che ti interessa e memorizza un dizionario tra il Tipo e la Descrizione. Ciò minimizzerà la riflessione (oltre all'ispezione del tipo di tempo di esecuzione, che non è poi così male). Il dizionario può essere archiviato come membro di qualsiasi classe che in genere necessita di tali informazioni o, se i client in tutto il dominio lo richiedono, tramite un oggetto singleton o di contesto.

Se è statico, esiste solo un'istanza della variabile, non vedo come l'ereditarietà avrebbe senso se potessimo fare ciò che vuoi realizzare con variabili statiche nelle classi derivate. Personalmente penso che stai andando molto lontano per cercare di evitare un'istanza var.

Perché non solo nel modo classico?

abstract class AbstractBase
{
    protected string _Description = "I am boring abstract default value";
}

class Foo : AbstractBase {

     public Foo() {
       _Description = "I am foo!";
     }
}

Non è statico se deve essere chiamato su un'istanza.

Se non lo stai chiamando su un'istanza, allora non c'è nessun polimorfismo in gioco (cioè ChildA.Description non è completamente correlato a ChildB.Description per quanto riguarda la lingua).

Una possibile soluzione consiste nel definire un Singleton della classe derivata nella classe base con l'aiuto di Generics.

import System;

public abstract class AbstractBase<T>
    where T : AbstractBase<T>, new()
{
    private static T _instance = new T();

    public abstract string Description { get; }
    public static string GetDescription()
    {
        return _instance.Description;
    }
}

public class DerivedClass : AbstractBase<DerivedClass>
{
    public override string Description => "This is the derived Class";
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(DerivedClass.GetDescription());
        Console.ReadKey();
    }
}

Il trucco è comunicare ai tuoi AbstractBase<T> alcuni dettagli su come DerivedClass è implementato:

  • È rinnovabile con where T: new() in modo da poter creare un'istanza Singleton
  • Deriva da se stesso con where T : AbstractBase<T>, quindi sa che ci sarà un'implementazione di Description

In questo modo _instance contiene il campo GetDescription() che può essere chiamato nel Metodo statico DerivedClass.GetDescription(). Ciò ti obbliga a sovrascrivere <=> nel tuo <=> e ti consente di chiamare il suo valore con <=>

Potresti fare il " abstract " il metodo base lancia un Exception, quindi uno sviluppatore è " avvisato " se tenta di invocare questo metodo su una classe figlio senza eseguire l'override.

Il rovescio della medaglia è che si potrebbe estendere la classe e non usare questo metodo. Quindi fai riferimento ad altre risposte fornite.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top