Ignorando la parte generica di un tipo mentre viene passato un oggetto

1

Ho una classe messaggio che contiene il nome di una destinazione e una variabile generica che funge da payload del messaggio

public class Message<T> {
    public string Destination
    public T Payload

    // ... constructors, functions, etc. ...
}

In questo modo, dovrei essere in grado di creare messaggi di qualsiasi tipo (senza dover elencare esplicitamente i tipi che sono validi) e mantenere comunque il test del compilatore sulla sicurezza del tipo.

Voglio creare un metodo che generi un messaggio. Il problema è che non voglio restituire un tipo specifico di messaggio, voglio solo restituire qualsiasi messaggio, o forse un array / elenco di messaggi con tipi diversi. Cioè invece di restituire esplicitamente Message<String> o Message<int> voglio solo che restituisca Message . In questo modo posso passare il messaggio alla destinazione, quindi la destinazione può capire di che tipo si tratta e consumare il carico utile.

Il problema è che, se lo faccio, comporterà un sacco di errori di typecasting e di tipo kinda il punto di usare i generici. Potrei anche creare Payload an object e poi convertirlo nella destinazione. O semplicemente usa dynamic . Dovrei o non dovrei usare object o dynamic in questo caso, e perché?

Questa è in realtà una riformulazione di una precedente domanda mal formulata che ho chiesto. In precedenza, invece di usare un tipo generico, usavo la dinamica per il messaggio, ma le risposte continuavano a dire che l'uso di dynamic non era adatto ad eccezione di cose come l'interoperabilità di Python, e molti si riferivano all'uso di generici che all'epoca non avevo pensare era adatto a causa della ragione di cui sopra. La mia domanda precedente è qui: Quando non usare la dinamica in C #

EDIT: come suggerito da Benjamin, ecco cosa mi piacerebbe accadere

Ho un "manager" che ha un sacco di oggetti modello in un dizionario. In esso c'è una funzione che va così:

void ManagerFunction()
{
    Message m = getMessage();
    objects[m.Destination].handleMessage(m)
}

Si noti che al gestore non interessa il payload.

Questo ha una funzione che genera un messaggio e lo restituisce.

Message getMessage()
{
    Message m;

    if(x)
        m = new Message<String>();
    else if(y)
        m = new Message<int>();
    // ... some more else-if's here ...
    else
        m  = new Message<double>();

    return m;
}

Si noti come la funzione di generazione dei messaggi può inviare un messaggio di qualsiasi tipo

Diciamo che la destinazione è costituita da oggetti che implementano la seguente interfaccia

interface DestinationInterface{
    void HandleMessage(Message m)
}

Mi piacerebbe che un oggetto potesse gestire l'interfaccia nel modo seguente

void HandleMessage(Message m)
{
    if(m.Payload is String){
        String s = (String) m.Payload
        // ...
    } else throw Exception();
}

E un altro oggetto che implementa l'interfaccia potrebbe gestire l'interfaccia in un modo diverso

void HandleMessage(Message m)
{
    if(m.Payload is int)
    {
        int i = (int) m.Payload
        // ...
    } else throw Exception();
}

A causa della tipizzazione, un tipo generico non sarebbe utile. Quindi dovrei usare l'oggetto per il carico utile? Perché non utilizzare la dinamica e sbarazzarsi di tutti i typecasting in primo luogo?

    
posta 9a3eedi 18.09.2014 - 09:52
fonte

1 risposta

2

Puoi saltare usando i messaggi generici e usa invece i gestori generici.

Ecco come lo facciamo:

public interface IMessage
{
}

public interface IHandle<in T> where T : IMessage
{
    void Handle(T message);
}

Implementa IMessage con "proprietà del payload" che descrive effettivamente il payload.

public class ConcreteMessage : IMessage
{
    ...
    public int BetterNameThanPayload { get; private set; }
}

Implementa un gestore di messaggi:

public class ConcreteHandler : IHandle<ConcreteMessage>
{
    public void Handle(ConcreteMessage message) 
    {
        var foo = message.BetterNameThanPayload;
        ...
    }
}

Usa la parola chiave dinamica quando chiami il gestore (questo dovrebbe essere l'unico posto in cui usare "dinamico"):

void ManagerFunction()
{
    IMessage m = getMessage();
    var messageHandlerType = typeof(IHandle<>).MakeGenericType(m.GetType());
    var messageHandler = [resolve message handler];
    ((dynamic)messageHandler).Handle((dynamic)message);
}

È possibile utilizzare una stringa "destinazione" per risolvere il gestore, ma se si utilizza un contenitore IOC è più semplice risolverlo per tipo (ed è sicuro per i tipi). Inoltre, saltare "destinazione" disaccoppierà il messaggio dal gestore.

    
risposta data 18.09.2014 - 13:02
fonte