Domanda

Dato il seguente albero ereditario, quale sarebbe il modo migliore per implementarlo in un modo che funzioni?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

Questo non riesce con: 'Bar.Bar ()' è inaccessibile a causa del suo livello di protezione .

Non voglio che il costruttore sia pubblico, solo le classi che ereditano da Foo dovrebbero essere in grado di creare Bar . Bar è un Foo specializzato e qualsiasi tipo di Foo dovrebbe essere in grado di crearne uno. Pubblico interno è un'opzione qui, poiché la maggior parte delle estensioni predefinite a Foo sarà interna alla DLL, ma considero questa una risposta sciatta, dal momento che chiunque arriva dopo che vuole creare il loro tipo di Foo o Baz (che è probabile che accada) sarà bloccato con un'implementazione predefinita CreateBar () , che può o può non soddisfare i loro bisogni.

Forse c'è un modo di refactoring per farlo funzionare bene? Sto sbattendo la testa sul muro cercando di progettarlo in modo che funzioni comunque.

Modifica (Altre informazioni):

Leggermente più concreto: Foo sta implementando IEnumerable e per farla breve, Bar sta fornendo la stessa interfaccia, ma a un sottoinsieme limitato di quell'oggetto enumerabile. Tutti i Foo dovrebbero essere in grado di creare sottoinsiemi di se stessi (es. Barra) e restituirli. Ma non voglio che tutti coloro che vogliono implementare un Foo debbano preoccuparsi di questo, perché Bar farà il proxy e si preoccuperà di limitare la gamma, ecc.

È stato utile?

Soluzione

Ok, nuova risposta:

  1. Dividi barra in un'interfaccia e una classe concreta.
  2. Esprimi il metodo astratto pubblico in termini di IBar.
  3. Rendi Bar una classe nidificata privata in Foo, implementando IBar. Dagli un costruttore interno che puoi chiamare da Foo.
  4. Scrivi un metodo protetto in Foo che crea un'istanza di Bar da sola. Le classi derivate da Foo possono usarlo per implementare il metodo astratto se solo il proxy è abbastanza buono e le classi con esigenze più complicate possono semplicemente implementare IBar direttamente. Puoi persino cambiare il metodo astratto in uno virtuale e creare una nuova barra da " questo " per impostazione predefinita.

EDIT: una variante su questo sarebbe quella di rendere Bar una classe nid protetta all'interno di Foo, con un costruttore pubblico. In questo modo qualsiasi classe derivata sarebbe in grado di crearne un'istanza, ma nessuna classe non correlata sarebbe in grado di "vedere". a tutti. Dovresti comunque separare l'interfaccia dall'implementazione (in modo che l'interfaccia possa essere pubblica) ma penso che sia comunque una buona cosa.

Altri suggerimenti

Sarebbe possibile rendere Baz un tipo nidificato all'interno di Bar? Questo è l'unico modo per dargli più accesso a Bar di quanto avrebbe altrimenti. Il solo fatto di avere la stessa classe genitore consente l'accesso solo ai membri protetti di Foo e Foo non ha un accesso speciale a Bar. Ho il sospetto che ci siano altri modi tortuosi di farlo con i tipi annidati, ma in realtà sarà abbastanza spiacevole per gli ingegneri della manutenzione.

Tuttavia, è abbastanza strano progettare una classe derivata per creare un'istanza di una classe diversa derivata dalla stessa classe base. È davvero quello che ti serve? Forse se lo metti in termini più concreti, sarebbe più facile trovare progetti alternativi.

Puoi accedere al costruttore di Bar tramite un tipo nidificato in Foo:

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}

Dimentica, per un momento, che Bar derivi da Foo. Se lo fai, sembra che il problema sia " Come posso farlo in modo che ogni sottoclasse di Foo possa creare una barra, anche se la sottoclasse non ha accesso al costruttore della barra? & Quot;

Questo è un problema abbastanza semplice da risolvere:

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

Se vuoi garantire che le sottoclassi di Foo non abbiano accesso al costruttore di Bar, inseriscile in un altro assemblaggio.

Ora, derivare Bar da Foo, è un semplice cambiamento:

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

" Non voglio che il costruttore sia pubblico. " Controllare.

" Solo le classi che ereditano da Foo dovrebbero essere in grado di creare Bar. " Controllare.

" Qualsiasi tipo di Foo dovrebbe essere in grado di crearne uno. " Controllare,

" Chiunque si presenti in seguito e desidera creare il proprio tipo di Foo o Baz (che è probabile che accada) sarà bloccato con un'implementazione predefinita di CreateBar (), che potrebbe o meno soddisfare le proprie esigenze. " Ciò dipende in larga misura da ciò che deve accadere nel metodo Foo.CreateBar (), credo.

C # non fornisce un equivalente diretto della parola chiave dell'amico C ++. Sembra che il tuo progetto richieda questo tipo di costrutto.

In C ++ puoi designare che una classe specifica ha accesso ai membri privati ??/ protetti di un'altra classe usando " amico " ;. Nota: questo non è lo stesso del modificatore interno di C # che consente di accedere a tutte le classi all'interno dello stesso assembly.

Guardando il tuo design, sembra che tu stia provando a fare qualcosa del genere che richiederebbe un amico in stile C ++. Jon Skeet ha ragione, il solito design in C # per compensare questo è usare una classe nidificata.

Questo post sul forum spiega ulteriormente e mostra alcuni esempi di come farlo.

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