2013-04-12 10 views
5

Questo fa parte del mio codice che ho bisogno di aiuto con:Bloccato su generici e interfacce. Hai bisogno di una soluzione basata su codice, forse riprogettazione delle interfacce

// simple service locator 
public class ServiceManager<TSvc> : IServiceManager<TSvc> where TSvc: class, IService 
{ 
    private Dictionary<object, TSvc> services; 

    public void RegisterService(TSvc service) 
    { 
     // omitted code here 
     this.services.Add(service.GetType(), service); // dictionary 
    } 
    public T GetService<T>() where T : TSvc 
    { 
     T result = default(T); 

     TSvc bufResult = null; 
     if (this.services.TryGetValue(typeof(T), out bufResult)) 
      result = (T)bufResult; 

     return result; 
    } 
    public TSvc GetService(Type serviceType) 
    { 
     TSvc result = null; 

     this.services.TryGetValue(serviceType, out result); 

     return result; 
    } 
} 

Poi i miei interfacce dominio:

public interface IItem 
{ 
    string Name { get; set; } 
} 
public interface IRepository<TModel> where TModel : IItem 
{ 
    new IEnumerable<TModel> GetItems(); 
    void InsertItem(TModel item); 
    void UpdateItem(TModel item); 
    void DeleteItem(TModel item); 
} 
public interface IService<TModel> where TModel : IItem 
{ 
    IRepository<TModel> Repository { get; } 
} 

poi alcuni dei miei classi di dominio:

public class Book: IItem 
{ 
    public string Name { get; set; } 
} 
public class BookRepo: IRepository<Book> 
{ 
    new IEnumerable<Book> GetItems(); 
    void InsertItem(Book item); 
    void UpdateItem(Book item); 
    void DeleteItem(Book item); 
} 
public class BookService: IService<Book> 
{ 
    IRepository<Book> IService<Book>.Repository { get { return this.Repository; } } 
    BookRepo Repository { get; set;} 
} 

Ora, se sono interessato ad utilizzare 'Bookservice' e fare qualcosa con esso, ho potuto ottenere dal localizzatore di servizio come questo:

public void DoSomething() 
{ 
    var bookService = serviceManager.GetService<BookService>(); 
    bookService.Repository.Insert(new Book()); 
} 

Ma il problema è che il tipo di servizio è noto solo in fase di esecuzione (ad es. selezione da combobox). Quindi, come sarebbe il metodo DoSomething?

public void DoSomething() 
{ 
    var typeOfService = combobox.SelectedValue.GetType(); // cbx of services 
    // ??? make use of serviceManager and typeOfService to get appropriate 'service' 
    service.Repository.Insert(/*new IITem here*/); 
} 

Inoltre, mi piacerebbe sapere come connettersi IService-IService<TModel> ... si potrebbe anche arrivare alla soluzione, ma non ho idea di come fare. L'interfaccia IService è vuota per il momento ...

Apprezzerei molto il tuo tempo. Per favore fatemi sapere se c'è qualcosa di poco chiaro! Grazie!

Aggiornamento: In base alle risposte, credo che la parte di riflessione potrebbe essere coinvolta (qualcosa come NSGaga ha sottolineato), ma ancora, senza collegare IService e IService<TModel> non riesco a ottenere quello che voglio. Chi ha idea di come ridisegnare questo?

+0

C'è qualche ragione per cui non stai usando un contenitore di dipendenze esistente come Ninject o Unity invece di crearne uno tuo? – Kenneth

+0

@Kenneth: il motivo dell'apprendimento sarebbe sufficiente? :) È una buona opportunità per imparare a manipolare le interfacce e usare i generici – Learner

+0

OK, comprensibile! Ho solo pensato che se cercavi solo una soluzione funzionante il più veloce possibile ti indico ad uno dei quadri. – Kenneth

risposta

2

Qualcosa del genere dovrebbe funzionare (digitando dalla mia testa, così avresti bisogno di controllare i dettagli di sintassi - ma dovrebbe dare il senso - o ti aggiungere in un secondo momento)

MethodInfo methodInfo = typeof(ServiceManager).GetMethod("GetService"); 
MethodInfo methodInfoGeneric = methodInfo.MakeGenericMethod(new[] { typeOfService }); 
methodInfoGeneric.Invoke(serviceManager, new object[] { }); 
+0

Quindi, modo di riflessione ... è davvero bello sapere questi dettagli ...Grazie! – Learner

+0

Prego Cristi - non c'è altro modo per "spostarsi" da "Tipo" in un mondo "fortemente tipizzato". – NSGaga

+0

btw, ho ancora bisogno in qualche modo di una connessione tra IService e IService , altrimenti non può funzionare. Come sarebbe il mio IService? – Learner

2

Per chiamare un metodo generico in cui il tipo è noto solo al runtime richiede l'uso di reflection.

Ad esempio, nell'ultimo esempio di codice, si presuppone giustamente che non è possibile chiamare GetService<BookService>, ma si sta ancora aggiungendo new Book() al servizio. Dovresti anche usare la riflessione per istanziare il nuovo oggetto, perché, ancora una volta, non conosci il tipo in fase di compilazione. Quindi è necessario utilizzare la riflessione almeno tre volte: una volta per chiamare il metodo che restituisce il servizio, una volta per creare il nuovo oggetto e una volta per chiamare il metodo di inserimento sul servizio.

È possibile isolare parte di questa complessità con dependency injection e inversion of control. Ad esempio, creare un metodo generico come questo:

void CreateNewObject<T>() where T : new() 
{ 
    var service = GetServiceFor<T>(); 
    service.Repository.Insert(new T()); 
} 

Ora, è sufficiente utilizzare riflessione una volta, per chiamare tale metodo, invece di tre volte.

+0

Punti molto buoni e interessanti! Sì, mi dispiace per quel "nuovo libro()" (copia-incolla) ... e immagino che la parte di riflessione che ho potuto ottenere da 'NSGaga' – Learner