2012-05-11 10 views
5

Ho creato una classe generica che ha bisogno di istanziare il suo tipo di implementazione, quindi il tipo di implementazione deve avere un parametro accessibile meno costruttore. Sembra che la restrizione new() possa fare il lavoro ma impone il tipo di implementazione per avere un costruttore pubblico quando è interno (ma accessibile in quanto entrambi sono sullo stesso assembly).C'è un modo per specificare la restrizione di T: new() ma con il costruttore interno?

  1. C'è un motivo per forzarlo a essere pubblico piuttosto che "accessibile"?
  2. C'è un modo per fare ciò di cui ho bisogno?

Grazie in anticipo.

EDIT: La ragione per fare questo è che ho una classe X che deve essere utilizzata attraverso un Singleton. La classe Singleton è una classe generica e voglio rendere interno il costruttore della classe X per evitare che utenti esterni accedano all'oggetto in modo errato (chiamando il costruttore).

+2

Rendi la classe interna e il costruttore pubblico. Se la classe deve essere pubblica, allora no non puoi. –

risposta

5

Questo non è consentito dal linguaggio C# come descritto nella sezione 4.4.3 della specifica Tipi associati e non associati.

Se il vincolo è il vincolo costruttore new(), il tipo A non deve essere abstract e deve avere un costruttore senza parametri pubblica. Questo è soddisfatto se uno dei seguenti è vero.

  • A è un tipo di valore, poiché tutti i tipi di valore hanno un costruttore predefinito pubblico
  • A è un parametro tipo avente il vincolo cosntructor
  • A è un parametro tipo avente il tipo di valore di vincolo
  • A è una classe che non è abstract e contiene un constatore esplicitamente dichiarato public senza parametri
  • A non è abstract e ha un costruttore predefinito.

Un errore del compilatore se una di queste condizioni non viene soddisfatta. Se ti accorgi di avere tipi che sono pubblici ma solo con costruttori interni, allora molto probabilmente dovrebbero essere effettivamente tipi interni con costruttori pubblici.

Si consiglia di modificare il tipo di accesso a internal e il suo costruttore su public e renderlo senza parametri. Il costruttore senza parametri public può quindi chiamare un costruttore non parametrico private o internal per eseguire ulteriori operazioni di inizializzazione.

internal class C<T> where : T new() 
{ 
    public C() : this(new T()) { 
    } 

    private C(T t) { 
     // Do additional initialization 
    } 
} 

Mente che modello è limitato, ma non c'è nulla ti impedisce di utilizzare un metodo di private invece.

internal class C<T> where T : new() { 
    public C() { 
     T t = new T(); 
     InitializeClass(t); 
    } 

    private void InitializeClass(T t) { 
     throw new NotImplementedException(); 
    } 
} 

Come per il vostro aggiornamento, ecco un piccolo esempio di un picchetto pubblico singleton.

public class Singleton<T> where T : new() 
{ 
    public static Singleton<T> Current { 
     get; 
     private set; 
    } 

    internal Singleton() : this(new T()) { 
    } 

    private Singleton(T t) { 
     Current = this; 
     // Do whatever you need to with T 
    }   

    public String Name { 
     get; 
     set; 
    } 
} 

Uso

// Somewhere in your internal assembly 
Singleton<String> singleton = new Singleton<String>(); 

// In an external assembly 
Singleton.Current.Name = "SoMoS"; 

Non c'è nemmeno bisogno di utilizzare costruttori in quel modo sia, si può altrettanto facilmente fare qualcosa di semplice.

public class Singleton<T> where T : new() 
{ 
    public static Singleton<T> Current { 
     get; 
     private set; 
    } 

    internal Singleton() { 
     T t = new T(); 
     // Do stuff with T 
    } 

    public String Name { 
     get; 
     set; 
    } 
} 

Generics non può essere la strada da percorrere se non è possibile progettare in base alle proprie esigenze. I generici possono fare così tanto e non risolvono ogni problema. Ci sono cose come Factory Pattern, Injection, ecc.

+0

Grazie, vedere la mia modifica per comprendere il motivo del costruttore interno –

+0

Aggiornamento della mia risposta. Potresti pensare un po 'di più al tuo progetto, perché i generici potrebbero non essere la soluzione migliore. –

2

Esiste un motivo per forzarlo a essere pubblico anziché "accessibile"?

Term accessibile è molto sensibile al contesto , generici non sono, dall'architettura. Nel tuo caso specifico internal potrebbe essere accessibile, ma i generici sono fatti per soluzioni generiche.

C'è un modo per fare ciò di cui ho bisogno?

In base al primo punto, no, non è possibile, ne sono a conoscenza.

+0

Bene, secondo il mio punto di vista il compilatore potrebbe verificare in fase di compilazione se il costruttore sarà accessibile o meno un errore solo se il nuovo non può essere chiamato dalla classe generica. Comunque, grazie per la spiegazione. –

+0

@SoMoS: Il problema è che un tipo 'T' che ha un vincolo' new() 'può essere passato a routine in diversi assembly. Mentre in alcuni casi potrebbe essere possibile per un compilatore determinare che un particolare parametro di tipo "T" non possa mai essere passato al di fuori di un particolare assemblaggio, in molti casi tale deduzione non è possibile. Consentire ai tipi con costruttori senza parametro 'internal' di soddisfare' nuovi() 'vincoli solo quando il compilatore potrebbe dedurre che sarebbe sicuro implicherebbe avere regole piuttosto strane per quando tale uso fosse permesso. – supercat

+0

@SoMos: ciò che si richiede è una speciale definizione 'internal new()', dove è possibile definirla esplicitamente. Perché non lo deduco ora? Penso che riguardi la condivisione di tipi tra diversi assembly.Cosa dovrebbe accadere se voglio utilizzare un generico di un tipo runtime che non è accessibile da "outside" .... c: – Tigran

1

I generici sono una soluzione generica, quindi se si utilizza il "nuovo" vincolo, la soluzione deve funzionare con ogni classe che implementa un responsabile pubblico.

Se si desidera implementare una soluzione generica per un tipo specifico di classi, è possibile definire una classe di base astratta che implementa un tale costruttore interno. Implementare una soluzione generica per questa classe di base astratta e utilizzare

* dove T: MyBaseClassWithInternalCtor *

come vincolo.

+0

Sembra una buona soluzione. Grazie! –

Problemi correlati