Elaborazione di richieste generiche

8

Ho un servizio web che accetta chiamate multiple che richiedono una gestione e una convalida diverse, usando i generici che sono riuscito a creare una classe comune che accetta un gestore e un validatore e assomiglia a questo

    public class PetitionService<T1, T2>
    where T1 : Headers, Petition
    where T2 : Headers
{
    PetitionHandler<T1, T2> petitionHandler;
    PetitionValidator<T1> petitionValidator;

    public PetitionService(PetitionHandler<T1, T2> handler, PetitionValidator<T1> validator)
    {
        if (handler == null)
        { throw new ArgumentNullException("petitionHandler cannot be null"); }
        if (validator == null)
        { throw new ArgumentNullException("petitionValidator cannot be null"); }

        petitionHandler = handler;
        petitionValidator = validator;
    }

    public T2 ProcessPetition(T1 petition)
    {
        petitionValidator.Validate(petition);
        return petitionHandler.Handle(petition);
    }
}

Va tutto bene e dandy, e funziona perfettamente, ma volevo aggiungere un altro livello su di esso. Una classe factory o un hub messaggi in cui la classe di servizio è mappata dall'ID tipo messaggio. Il problema è che, poiché ogni gestore restituisce un figlio diverso di intestazioni, il chiamante deve annullare la risposta. C'è un modo per evitare l'unboxing, è unboxing non così male o sto solo esagerando?

    
posta Zalomon 08.06.2016 - 12:51
fonte

2 risposte

0

I wanted to add another layer on top of it. Either a factory class or a message hub where the service class is mapped by the message type id. The problem with that is that since each handler return a different child of Headers it'll lead to the caller needing to unbox the response. I

OP, se capisco la tua domanda (devo leggere tra le righe), vuoi una fabbrica che scelga quale tipo di PetitionService restituire scegliendo da una tabella di ricerca. Quella tabella avrebbe una chiave di dizionario di ID messaggio e il valore sarebbe un tipo base di qualche tipo che è comune a tutti i vari servizi di petizione. Poiché il dizionario contiene il tipo di base, sei preoccupato di trasmettere ogni voce del dizionario al suo tipo più derivato per accedere alle proprietà specifiche delle intestazioni e delle petizioni.

(Questo non è il pugilato o l'unboxing ... che ha a che fare con il passare di tipi di valori in cui è necessario un oggetto).

La risposta breve è ... sì, lo stai pensando troppo. Non è un grosso problema lanciare da un tipo di base a un tipo derivato, è piuttosto comune nella programmazione orientata agli oggetti.

La risposta più lunga è ... sì, puoi evitare il cast, usando la contravarietà, ma è piuttosto complicato (per me, comunque). Ho risposto a una domanda simile per un altro poster poche settimane fa. Mi ci sono voluti diversi tentativi per farlo bene. Se desideri verificarlo, leggi questa risposta , quella intitolata" quarta volta è il fascino ".

Forse un poster di genere che è migliore con i generici di quello che posso offrire con un esempio più semplice.

    
risposta data 21.03.2017 - 07:22
fonte
0

La tua domanda e la tua preoccupazione sono confuse.

Sto semplificando eccessivamente, ma in C #, i vincoli di tipo generico come T1 e T2 sono generalmente di tipo "base-classe, interfaccia1, interfaccia2 ..." o "interfaccia1, interfaccia2 ..." o "struct, interface1, interface2 ..."

Vincoli sui parametri del tipo

Poiché non è possibile ricavare tipi di struttura / valore, presumo quindi che i vincoli "dove: ..." su Intestazioni e Petizione riguardino le interfacce, se non altro per "dove: Intestazioni, Petizione" e hai menzionato che ci sarebbe "... un diverso figlio di intestazioni".

Una convenzione di denominazione più comune, btw, consiste nell'iniziare i nomi dei tipi di interfaccia con "I", quindi "IHeaders" e "IPetition" potrebbero aiutare il lettore, imo.

Ora, per quanto riguarda il boxing / unboxing, così come sta nel tuo codice (e come ha detto Frank), non c'è nulla che richieda al compilatore (ancora) di preoccuparsene.

Le cose sarebbero diverse se tu avessi da qualche parte, ad esempio un

DoSomething(object value)

e quindi avresti un sito di chiamata come

public T2 ProcessPetition(T1 petition)
{
    DoSomething(petition);
    // etc
}

allora sì, a seconda che T1 venga chiuso da un tipo di classe (di riferimento), ad esempio

class PetitionObject : IHeaders, IPetition { ... }

var service = new PetitionService<PetitionObject, ...>(...)
// ...
service.ProcessPetition(new PetitionObject(...));

vs. un tipo struct / value, ad esempio

struct PetitionDatum : IHeaders, IPetition { ... }

var service = new PetitionService<PetitionDatum, ...>(...)
// ...
service.ProcessPetition(new PetitionDatum(...));

la boxe dovrebbe essere eseguita nel sito di chiamata DoSomething () in ProcessPetition (...), in quest'ultimo caso - a causa di

object value

argomento formale e perché PetitionDatum è un tipo di valore, precisamente (in modo che "valore" possa diventare un riferimento a una copia inscatolata dell'istanza PetitionDatum fornita dal chiamante quando viene eseguito il corpo di DoSomething ()).

Potresti confondere qualche altro concetto con a quale "boxing" si fa comunemente riferimento .

'HTH,

    
risposta data 22.09.2016 - 00:39
fonte

Leggi altre domande sui tag