Uso dei generici per i vincoli di risoluzione delle dipendenze

0

Ho un tale problema: l'interfaccia senza parametri in e out.

public interface IIndexer
{
    void FillIndex();
}

Dovrebbe funzionare diversamente per diversi tipi di attività (commenti, Mi piace e così via). E quando applico il principio IoC tramite l'iniezione di dipendenza ( via Ninject ), il mio risolutore non può risolvermi CommentsIndexer, Mi piaceIndexer perché provo a iniettarli come tipo IIndexer.

Ho due soluzioni per la testa:

  1. Derivazione di molte interfacce da IIndexer per ogni attività e quindi implementare ciascuno di essi nelle mie classi (questa opzione disordine codice con molte interfacce vuote)
  2. Utilizza i generici per differenziarli

_

public interface IIndexer<T>
    where T : Activity
{
    void FillIndex();
}

Ma in questo caso IDE mi dice che il parametro Type T non è mai usato

La domanda è: è corretto usare i generici in casi come questo (per distinguere solo il tipo)? Grazie!

    
posta Mykhailo Kilianovskyi 05.09.2017 - 09:32
fonte

2 risposte

0

But in this case IDE says me that Type parameter T is never used

Nella maggior parte dei casi, è davvero molto strano dichiarare un tipo generico e quindi non usarlo. Tuttavia, qui c'è un motivo significativo potenzialmente .

Supponiamo di avere una singola classe che, per qualche ragione, indicizza sia i Mi piace che i commenti (questa non è sempre una violazione di SRP, anche se sembra in questo esempio semplificato)

Con il tuo esempio non generico, potresti riscontrare un problema:

public class CommentsAndLikesIndexer : IIndexer
{
    public void FillIndex()
    {
         Console.WriteLine("index filled!");
    }
}

var myIndexer = new CommentsAndLikesIndexer();

Non c'è modo di segnalare che questo può essere usato per l'indicizzazione di entrambi i commenti e i Mi piace. Non c'è nemmeno modo di indicizzare solo commenti (o "mi piace") se entrambi sono implementati (a parte alcune proprietà booleane non intuitive che vanno al di fuori dell'interfaccia stessa).

Non c'è anche modo di capire che cosa myIndexer è in grado di indicizzare.

Quando si utilizza un'interfaccia generica, è possibile specificare quali entità possono essere indicizzate:

public class CommentsAndLikesIndexer : IIndexer<Comment>, IIndexer<Like>
{
    public void FillIndex()
    {
         Console.WriteLine("index filled!");
    }
}

var myIndexer = new CommentsAndLikesIndexer();

Ora abbiamo un modo per testare cosa può essere indicizzato da myIndexer :

bool isLikeIndexer    = myIndexer is IIndexer<Like>;
bool isCommentIndexer = myIndexer is IIndexer<Comment>;

Tuttavia, il problema riguarda ancora il caso in cui FillIndex() verrà utilizzato per entrambi i casi.

Forse è quello che vuoi. Ma supponiamo che tu voglia che siano separati. Puoi ottenere ciò utilizzando le dichiarazioni esplicite dell'interfaccia:

public class CommentsAndLikesIndexer : IIndexer<Comment>, IIndexer<Like>
{
    public void IIndexer<Comment>.FillIndex()
    {
         Console.WriteLine("comment index filled!");
    }

    public void IIndexer<Like>.FillIndex()
    {
         Console.WriteLine("like index filled!");
    }
}

E ora abbiamo implementazioni separate per un'interfaccia doppiamente implementata!

Alcuni sidenotes:

  • Il downside è che quando un'interfaccia viene esplicitamente implementata, devi sempre usare l'interfaccia come tipo della variabile, per chiarire il quale metodo che vuoi usare.
    • CommentsAndLikesIndexer myIndexer = new CommentsAndLikesIndexer(); myIndexer.FillIndex(); non funziona. Il compilatore non conosce il metodo che desideri.
    • IIndexer<Comment> myIndexer = new CommentsAndLikesIndexer(); myIndexer.FillIndex(); funziona.
    • Per estensione, anche% co_de dovrebbe funzionare.
  • Se la tua firma del metodo sarebbe univoca in base al parametro generico (ad esempio CommentsAndLikesIndexer myIndexer = new CommentsAndLikesIndexer(); (myIndexer as IIndexer<Comment>).FillIndex(); ), potresti eseguire questa operazione senza implementazione esplicita dell'interfaccia, poiché i due metodi non saranno in conflitto.

Is it OK to use generics in cases like this (for type distinguish only)?

Quanto sopra detto, un'interfaccia generica che non usa il suo parametro di tipo generico è una cosa insolita da vedere. Esistono casi d'uso marginali per questo, ma suggerisco caldamente di verificare se questo è l'approccio migliore per te.

Se il tuo metodo è effettivamente corretto come previsto (nessun parametro di input), allora questo può essere un approccio valido.

    
risposta data 03.05.2018 - 16:07
fonte
0

Se si tratta di un evento, dovresti trattarlo come tale. Crea un pattern osservatore / sottoscrittore in cui il chiamante passa un'interfaccia EventHandler per un evento specifico e quando si verifica tale evento, chiama qualsiasi e tutti gli EventHandler. Per differenziare gli eventi, potresti passare una classe EventArgs con informazioni relative all'evento in questione, anche se inizialmente solo il nome dell'evento, ma potresti facilmente espanderlo più tardi.

In alternativa potresti semplicemente creare un delegato di eventi per ogni tipo di evento.

In entrambi i casi, la classe che attiva l'evento non sa nulla della classe che esegue l'attività, che è ciò che si desidera. Non devi necessariamente usare una libreria di dipendenze come Ninject per questo.

    
risposta data 05.09.2017 - 10:30
fonte

Leggi altre domande sui tag