Domanda

Esiste un modo per forzare una classe (figlio) ad avere costruttori con firme particolari o metodi statici particolari in C # o Java?

Ovviamente non puoi usare le interfacce per questo, e so che avrà un uso limitato. Un'istanza in cui la trovo utile è quando si desidera applicare alcune linee guida di progettazione, ad esempio:

Eccezioni
Dovrebbero tutti avere i quattro costruttori canonici, ma non c'è modo di imporlo. Devi fare affidamento su uno strumento come FxCop (caso C #) per catturarli.

Operatori
Non esiste un contratto che specifichi che è possibile sommare due classi (con operatore + in C #)

Esiste un modello di progettazione per aggirare questa limitazione? Quale costrutto potrebbe essere aggiunto al linguaggio per superare questa limitazione nelle future versioni di C # o Java?

È stato utile?

Soluzione

Non applicato al momento della compilazione, ma ho trascorso molto tempo a esaminare problemi simili; una libreria matematica abilitata per generici e un'efficace (non -default) ctor API sono entrambi disponibili in MiscUtil . Tuttavia, questi vengono controllati solo al primo utilizzo in fase di esecuzione. In realtà questo non è un grosso problema: i test delle unità dovrebbero trovare molto rapidamente eventuali operatori / cettori mancanti. Ma funziona e molto rapidamente ...

Altri suggerimenti

Usando i generici puoi forzare un argomento di tipo ad avere un costruttore senza parametri, ma questo è il limite di esso.

Oltre a quelli generici, sarebbe difficile usare queste restrizioni anche se esistessero, ma a volte potrebbe essere utile per parametri / argomenti di tipo. Consentire ai membri statici nelle interfacce (o possibilmente interfacce statiche) potrebbe anche aiutare con l'operatore numerico "generico" problema.

I ha scritto di questo poco fa di fronte a un problema simile.

È possibile utilizzare il modello Factory.

interface Fruit{}

interface FruitFactory<F extends Fruit>{
   F newFruit(String color,double weight);

   Cocktail mixFruits(F f1,F f2);
}

È quindi possibile creare classi per qualsiasi tipo di frutta

class Apple implements Fruit{}
class AppleFactory implements FruitFactory<Apple>{
   public Apple newFruit(String color, double weight){
       // create an instance
   }
   public Cocktail mixFruits(Apple f1,Apple f2){
       // implementation
   }
}

Ciò non impone che non sia possibile creare un'istanza in un modo diverso dall'uso di Factory ma almeno è possibile specificare quali metodi si richiederebbe a una Factory.

Costruttori di forze

Non puoi. Il più vicino che puoi venire è rendere privato il costruttore predefinito e quindi fornire un costruttore con parametri. Ma ha ancora delle lacune.

class Base
{
  private Base() { }
  public Base(int x) {}
}

class Derived : Base
{
  //public Derived() { } won't compile because Base() is private
  public Derived(int x) :base(x) {}
  public Derived() : base (0) {} // still works because you are giving a value to base
}

Il problema nel linguaggio è che i metodi statici sono in realtà cittadini di seconda classe (Un costruttore è anche una specie di metodo statico, perché non è necessario un esempio per iniziare).

I metodi statici sono solo metodi globali con uno spazio dei nomi, in realtà non " appartengono " alla classe in cui sono definiti (OK, hanno accesso a metodi privati ??(statici) nella classe, ma questo è tutto).

Il problema a livello di compilatore è che senza un'istanza di classe non si dispone di una tabella di funzioni virtuali, il che significa che non è possibile utilizzare tutta l'eredità e il polimorfismo.

Penso che si potrebbe farlo funzionare aggiungendo una tabella virtuale globale / statica per ogni classe, ma se non è stato ancora fatto, probabilmente c'è una buona ragione per farlo.

Ecco, lo risolverei se fossi un progettista di lingue.

Consenti alle interfacce di includere metodi, operatori e costruttori statici.

interface IFoo  
{  
  IFoo(int gottaHaveThis);  
  static Bar();  
}

interface ISummable
{
      operator+(ISummable a, ISummable b);
}

Non consentire il corrispondente nuovo IFoo (someInt) o IFoo.Bar()

Consenti l'ereditarietà dei costruttori (proprio come i metodi statici).

class Foo: IFoo
{
  Foo(int gottaHaveThis) {};
  static Bar() {};
}

class SonOfFoo: Foo 
{
  // SonOfFoo(int gottaHaveThis): base(gottaHaveThis); is implicitly defined
}

class DaughterOfFoo: Foo
{
  DaughhterOfFoo (int gottaHaveThis) {};
}

Consenti al programmatore di trasmettere interfacce e verificare, se necessario, in fase di esecuzione se il cast è semanticamente valido anche se la classe non specifica esplicitamente.

ISummable PassedFirstGrade = (ISummable) 10; 

Sfortunatamente non puoi in C #. Ecco un pugno però:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Foo.Instance.GetHelloWorld());
        Console.ReadLine();
    }
}

public class Foo : FooStaticContract<FooFactory>
{
    public Foo() // Non-static ctor.
    {
    }

    internal Foo(bool st) // Overloaded, parameter not used.
    {
    }

    public override string GetHelloWorld()
    {
        return "Hello World";
    }
}

public class FooFactory : IStaticContractFactory<Foo>
{
    #region StaticContractFactory<Foo> Members

    public Foo CreateInstance()
    {
        return new Foo(true); // Call static ctor.
    }

    #endregion
}

public interface IStaticContractFactory<T>
{
    T CreateInstance();
}

public abstract class StaticContract<T, Factory>
    where Factory : IStaticContractFactory<T>, new() 
    where T : class
{
    private static Factory _factory = new Factory();

    private static T _instance;
    /// <summary>
    /// Gets an instance of this class. 
    /// </summary>
    public static T Instance
    {
        get
        {
            // Scary.
            if (Interlocked.CompareExchange(ref _instance, null, null) == null)
            {
                T instance = _factory.CreateInstance();
                Interlocked.CompareExchange(ref _instance, instance, null);
            }
            return _instance;
        }
    }
}

public abstract class FooStaticContract<Factory>
    : StaticContract<Foo, Factory>
    where Factory : IStaticContractFactory<Foo>, new() 
{
    public abstract string GetHelloWorld();
}

Bene, so dal testo della tua domanda che stai cercando un'applicazione per la compilazione in tempo. A meno che qualcun altro non abbia un brillante suggerimento / hack che ti consentirà di farlo nel modo in cui stai implicando il compilatore, suggerirei che potresti scrivere un'attività MSbuild personalizzata che ha fatto questo. Un framework AOP come PostSharp potrebbe aiutarti a realizzarlo a tempo pieno sostenendo il suo modello di task di costruzione.

Ma cosa c'è di sbagliato nell'analisi del codice o nell'applicazione del runtime? Forse è solo una preferenza e lo rispetto, ma personalmente non ho problemi con il fatto che CA / FXCop controlli queste cose ... e se vuoi davvero forzare gli implementatori a valle delle tue classi ad avere firme di costruttore, puoi sempre aggiungere regole run- controllo del tempo nel costruttore della classe base usando reflection.

Richard

Non sono sicuro di ciò che stai cercando di ottenere, puoi per favore elaborare? L'unico motivo per forzare un costruttore specifico o un metodo statico in diverse classi è tentare di eseguirli dinamicamente in fase di esecuzione, è corretto?

Un costruttore deve essere specifico per una particolare classe, in quanto ha lo scopo di inizializzare le esigenze specifiche della classe. A quanto ho capito, il motivo per cui vorresti imporre qualcosa in una gerarchia o un'interfaccia di classe è che si tratta di un'attività / operazione rilevante per il processo in corso, ma può variare in circostanze diverse. Credo che questo sia il beneficio previsto del polimorfismo, che non è possibile ottenere utilizzando metodi statici.

Richiederebbe anche la conoscenza del tipo specifico della classe per cui si desidera chiamare il metodo statico, che spezzerebbe tutto il nascondimento polimorfico delle differenze di comportamento che l'interfaccia o la classe astratta sta cercando di ottenere.

Se il comportamento rappresentato dal costruttore è destinato a far parte del contratto tra il cliente di queste classi, lo aggiungerei esplicitamente all'interfaccia.

Se una gerarchia di classi ha requisiti di inizializzazione simili, utilizzerei una classe base astratta, tuttavia dovrebbe spettare alle classi ereditarie il modo in cui trovano il parametro per quel costruttore, che può includere l'esposizione di un costruttore simile o identico.

/ p>

Se questo ha lo scopo di permetterti di creare diverse istanze in fase di runtime, allora consiglierei di usare un metodo statico su una classe base astratta che conosca le diverse esigenze di tutte le classi concrete (per questo potresti usare l'iniezione delle dipendenze) .

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