2014-12-15 7 views
9

ho una classe definita come:bambino non chiamato quando l'elemento di base accessibile

public class DatabaseEntity<T> where T : DatabaseEntity<T> { 
    public static string Query { get; protected set; } 
    public static IList<T> Load() { 
     return Database.Get(Query); 
    } 
} 

public class Node : DatabaseEntity<Node> { 
    static Node() { 
     Node.Query = @"SELECT Id FROM Node"; 
    } 
} 

Quando eseguo Node.Load() da un codebehind (Window.xaml.cs) costruttore statico del nodo mai incendi; o almeno non ha colpito un breakpoint e non imposta Node.Query su qualcosa di diverso da null.

C'è qualche motivo per cui questo potrebbe accadere?

Soluzione

Scopri i migliori risposte qui sotto per un paio di soluzioni. Nel mio caso, ho deciso di rendere pubblica la variabile Query e di impostare tutte le istanze di Query in un unico punto. (Non ideale, ma funziona.)

+0

Viene serializzato tramite DataContractSerializer? Come ricordo che DCS non licenzia i costruttori. – PhillipH

+0

Perché non fare semplicemente query 'private const string Query = [query];'? – atlaste

+0

Non lo è. Tuttavia, 'Load()' è in realtà una funzione statica ereditata. Questo farebbe la differenza? –

risposta

4

Il problema sta nelle ipotesi su quando viene chiamato un costruttore statico. Il documentation, che non è il più chiaro, afferma che

Viene chiamato automaticamente prima che venga creata la prima istanza o che venga fatto riferimento a qualsiasi membro statico.

Si può supporre che se si chiama

Node.Load(); 

che si sta chiamando un metodo statico della classe Node, ma in realtà si sta chiamando sulla classe di base, come quello è dove è implementato.

Quindi, per risolvere questo problema, hai due possibilità. In primo luogo, è possibile attivare il costruttore statico in modo esplicito con la creazione di una nuova istanza della classe Node prima di chiamare Load()

var foo = new Node(); // static ctor triggered 
Node.Load(); 

o creare un membro virtuale protetto che la classe base può chiamare al fine di ottenere il valore di query (possibile 't uso astratto qui, sfortunatamente)

public class DatabaseEntity<T> where T : Derp { 
    protected abstract string Query { get; } 
    public static IList<T> Load() {   
     return Database.Get(new DatabaseEntity<T>().Query); 
    } 
} 

Entrambe le quali sono hacky. Meglio rinunciare alla statica del tutto e andare con i metodi di istanza. La statica dovrebbe essere usata con parsimonia, in quanto provocano un accoppiamento stretto e altri mal di testa del design come questo.

4

Sì, i costruttori statici non verranno chiamati fino a quando i membri della classe non vengono prima acceduti o prima viene creata la prima istanza.

Nel tuo caso stai accedendo a DatabaseEntity<T>.Load, così il costruttore statico di DatabaseEntity<T> non sarà chiamato classe derivata.

Anche se si chiama Node.Load è mappato a DatabaseEntity<Node> in fase di compilazione. Quindi tecnicamente non hai accesso alla classe Node.

+0

Questo è corretto, puoi dimostrare questo comportamento creando un'istanza della classe nodo prima di chiamare Node.Load() e dovrebbe funzionare. Se non viene effettuata alcuna istanza prima della chiamata, non lo farà. –

+0

Questa è una grande spiegazione. Potete offrire qualche suggerimento o consiglio su come risolvere questo problema? (Oltre all'ovvio, 'Node n = new Node();') –

+2

@CharlesW Sospetto che si tratti di un problema di progettazione. Senza sapere di più su cosa stai cercando di ottenere, non posso fare a meno di questo. Una soluzione ovvia sarà quella di aggiungere il metodo 'Node.Load' con un nuovo modificatore che richiama semplicemente l'implementazione della classe base, ma. Non lo consiglio –

Problemi correlati