Un piccolo serpente ci conduce attraverso le principali caratteristiche del Windows Phone 7
Introduzione Core UI & Design Conclusione Premi Discussioni
Marcosroom.it Didatticando

Castle.Windsor è eccezionale, ma ...

... ma non può essere utilizzato sui device Windows Phone 7. Si, hai letto bene. Castle.Windsor non può essere utilizzato sui device Windows Phone 7.

Uhm... e ora? Cosa possiamo fare?

All'inizio ho cercato un'altra versione Castle.Windsor per Windows Phone 7, ma non ho trovato nulla di interessante. A questo punto ho iniziato a preoccuparmi. Fortunatamente, ho avuto un'idea: farò io il mio contenitore! Un contenitore può sembrare lungo e complesso, ma una classe che espone solo le funzionalità di base è molto semplice.

Nel nostro caso, un contenitore semplice serebbe stato sufficiente, ma, poichè sono pazzo, ho scritto un contenitore con qualche altra funzione in più. Eccole:

  Nome del metodo Descrizione
Method ChangeRegistration<T>(Type)

Cambia la registrazione per il tipo T. Se il tipo T non è registrato, verrà registrato

Method Deregister<T>()

Deregistra il tipo T

Method GetComponentTypeForService<T>() + 1 ovrl.

Ritorna il tipo associato al servizio di tipo T

Method Register(WP7ContainerEntry) + 4 ovrl.

Registra una nuova voce nel contenitore

Method Resolve<T>() + 1 ovrl.

Risolve il tipo T

Un altro importante oggetto da analizzare è WP7ContainerEntry:

  Nome del membro Descrizione
Property Component

Tipo del componente rappresentato da questa voce

Property Params

Dictionary<string, object> contenente le proprietà da iniettare (usato anche per iniettare parametri nel costruttore)

Property Service

Tipo del servizio rappresentato da questa voce

Method For<T>() + 1 ovrl.

Metodo statico che crea una nuova WP7ContainerEntry per il tipo T

Method ImplementedBy<T>() + 1 ovrl.

Imposta la proprietà Component

Method Parameters(Dictionary<string, object>)

Imposta la proprietà Params

Invece di scrivere tutte queste parole inutili, lasciamo che sia il codice a parlare: vediamo un esempio di utilizzo di queste classi.
Immaginiamo di avere qualcosa simile a questo:

Example for the WindowsPhone 7 Container

In poche parole, abbiamo una classe, MyClass, la quale eredita da MyBaseClass e implementa IMyInterface; il costruttore di questa classe ha bisogno di un UsefulObject come parametro. In uno scenario come questo, è possibile utilizzare il contenitore in molti modi, per esempio, questo:

//Crea il contenitore
WP7Container container = new WP7Container();

//Registra UsefulObject
container.Register<UsefulObject>();

//Registra MyClass come implementazione di IMyInterface
container.Register<IMyInterface, MyClass>();

//Risolve l'oggetto
IMyInterface myObj = container.Resolve<IMyInterface>();

La prima cosa che dobbiamo fare è creare una nuova istanza del contenitore, e poi, registrare tutti i tipi di cui abbiamo bisogno. Quando vuoi, ora puoi chiamare il metodo Resolve, e ottenere un'istanza dei tipi registrati. Una cosa da notare è che non abbiamo bisogno di passare nessun parametro al costruttore della classe: il contenitore trova automaticamente il costruttore che possiamo chiamare, passandogli qualunque cosa di cui abbia bisogno, anche risolvendo altri tipi. In questo caso, il contenitore nota che l'unico costruttore disponibile necessita di un UsefulObject, quindi,per creare un uovo oggetto MyClass, passa al costruttore un nuovo UsefulObject.
Se non c'è nessun costruttore che il contenitore può chiamare, viene sollevata un'eccezione.

//Crea il contenitore
WP7Container container = new WP7Container();

//Registra l'interfaccia e le sue implementazioni
container.Register<IMyInterface, MyClass>(new Dictionary<string, object>() { 
    { "PropertyOfTheInterface", "interface" }, 
    { "PropertyOfTheClass", 10 }, 
    { "PropertyOfTheBaseClass", "base" }
});

//Registra UsefulObject
container.Register<UsefulObject>(new Dictionary<string, object>() { 
    { "UsefulProperty", "injected!" }
});

//Risolve UsefulObject
UsefulObject usefulObj = container.Resolve<UsefulObject>();

//Risolve IMyInterface
IMyInterface obj = container.Resolve<IMyInterface>();

Questa volta, invece, quando registriamo i tipi, specifichiamo come parametri il nome della proprietà che vogliamo iniettare e il suo valore:
in questo modo, quando chiamiamo Resolve, il nostro oggetto ha già alcune proprietà impostate. I parametri possono essere anche utilizzati per iniettare parametri ai costruttori: in questo caso, però, dobbiamo specificare il nome del parametro del costruttore e il valore.
La chiamata a Resolve<UsefulObject> restituisce un nuovo UsefulObject, dove UsefulProperty è impostata a "injected!".
La chiamata a Resolve<IMyInterface>, invece, crea un nuovo oggetto MyClass e ne imposta le proprietà (possono appartenere alla classe base, e possono essere l'implementazione di un'interfaccia; non importa); durante la chiamata al costruttore, viene chiamato Resolve<UsefulObject>: in questo modo, sarà passato al costruttore di MyClass un nuovo UsefulObject con la proprietà UsefulProperty pari a "injected!". Infatti, se si valuta ((MyClass)obj).UsefulStructFromTheConstructor.UsefulProperty, si nota che la proprietà ha valore "injected!".

Ciò che il WP7Container fa è nulla, se lo confrontiamo con Castle.Windsor, ma penso sia sufficiente per una piccola classe come questa, giusto? Ma ora è tempo di scavare un po' più in profondità nelle classi per scoprire come funzionano.

Container for Windows Phone 7

WP7ContainerEntry è solo una classe contenente un po' di dati, quindi non necessita di molte spiegazioni; al contrario, WP7Container può essere un po' più interessante. Questa è l'idea di base: abbiamo una lista di WP7ContainerEntry; dovremmo quindi essere in grado di aggiungere e rimuovere voci da questa lista (utilizzando i metodi Register e Deregister) e di risolvere un tipo (metodo Resolve).
Il cuore dell'intera classe è il metodo ResolveEntry: transforma una WP7ContainerEntry nell'oggetto che rappresenta, trova il giusto costruttore e inietta i parametri.

Potrebbe essere un po' difficoltoso scrivere un metodo come questo, ma se sai cos'è la reflection, il gioco è fatto. Comunque, ecco il codice:

//Sceglie il costruttore da chiamare
ConstructorInfo ctor = 
    entry.Component.GetConstructors()
    .FirstOrDefault(x => 
        x.GetParameters().All(y => 
            (entry.Params.ContainsKey(y.Name) && y.ParameterType.IsAssignableFrom(entry.Params[y.Name].GetType()))
            || IsThereEntryForType(y.ParameterType)
        )
    );

//Controlla che il costruttore sia stato trovato
if (ctor == null)
    throw new ArgumentException(string.Format(
        "Cannot create type {0}: constructor not found. Try with different parameters.", 
        entry.Component.FullName
    ));

//Chiama il ctor
ParameterInfo[] ctorParams = ctor.GetParameters();
object[] pars = new object[ctorParams.Count()];
for (int i = 0; i < pars.Length; i++) {
    ParameterInfo p = ctorParams[i];
    if (entry.Params.ContainsKey(p.Name))
        pars[i] = entry.Params[p.Name];
    else

        pars[i] = this.Resolve(p.ParameterType);
}
object obj = ctor.Invoke(pars);

//Controlla se ci sono delle proprietà da impostare
foreach (var x in entry.Params) {
    PropertyInfo prop = entry.Component.GetProperty(x.Key);
    if (prop != null) {
        if (!prop.PropertyType.IsAssignableFrom(x.Value.GetType()))
            throw new InvalidCastException(string.Format(
                "Cannot cast from {0} to {1}", 
                x.Value.GetType().FullName,
                prop.PropertyType.FullName
            ));
        else

            prop.SetValue(obj, x.Value, null);
    }
}

//Ritorna
return obj;

Ciò che vogliamo fare è creare una nuova istanza del componente della voce, quindi, ciò che dobbiamo fare è trovare un costruttore che possiamo chiamare. Per raggiungere questo obiettivo, scorriamo tutti i costruttori disponibili del tipo e troviamo il primo i cui parametri sono tutti disponibili, o perchè sono nel dizionario dei parametri, o perchè sono registrati nel contenitore. Una volta trovato il costruttore, dobbiamo trovare i parametri: ne controlliamo uno alla volta e, se è nel dizionario, prendiamo il valore corrispondente, altrimenti, risolviamo il tipo. Il passo successivo è creare l'oggetto e memorizzarlo in una variabile. L'ultimo punto è iniettare le proprietà: scorriamo lungo tutte le voci disponibili del dizionario e controlliamo se possiamo impostare il valore della proprietà.

Come ho detto prima, questa classe ha caratteristiche limitate: ti consiglio di utilizzarla solo se non puoi usare Castle.Windsor, anche se ho provato a mantenere la stessa sintassi.

 

Condividi
Indietro Tutti i webmaster che volessero segnalare, non copiare,
il contenuto di questa pagina sul proprio sito, possono farlo liberamente.
E' gradito un preavviso tramite mail all'autore e l'iserimento,
nella pagina di citazione, di un link verso la pagina corrente.
© Copyright    Marco's Room
Avanti
Download SnakeMobile

Scaricato 115 volte

Celle IoC (Inversione di controllo) Un contenitore per Windows Phone 7 Controller di movimento Obiettivi Livelli Serpenti nemici Salvataggio e caricamento dei livelli