2015-07-01 8 views
7

Sto tentando di scrivere un sistema di passaggio di messaggistica generico per Delphi e .NET. Il sistema consente di definire i messaggi come oggetti semplici e i gestori di messaggi sono definiti come metodi anonimi che agiscono su tali oggetti.Come posso memorizzare i parametri di tipo di un metodo parametrizzato e poi usarli per convertire un oggetto JSON in un oggetto semplice di tipo generico?

Gli oggetti vengono convertiti in JSON e passati tra le applicazioni in esecuzione sulla stessa macchina. Ogni applicazione gestisce un elenco di gestori che comprendono tipi di messaggi specifici.

La mia classe ha un numero di metodi di registrazione parametrizzati. Alcuni prendono un singolo parametro di tipo. Alcuni prendono un paio di parametri dove uno rappresenta un oggetto richiesta e l'altro un oggetto risposta. Ecco ciò che il richiesta/risposta di registrazione del gestore assomiglia:

procedure TTelegraph.RegisterRequestHandler<TRequest, TResponse> 
    (requestTypeToHandle: string; handler: TFunc<TRequest, TResponse>); 
begin 
    FRequestHandlers.Add(requestTypeToHandle, 
    TRequestHandler<TRequest, TResponse>.Create(handler, 
    TRequest, 
    TResponse)); 
end; 

FRequestHandlers è un TDictionary<string,TRequestHandler>. Il metodo di registrazione viene chiamato in questo modo:

FTelegraph.RegisterRequestHandler<TTestRequest, TTestResponse>('My Request', 
    function(x: TTestRequest): TTestResponse 
    begin 
     Result := TTestResponse.Create; 
     Result.Number := x.Number; 
     Result.Message := Format('Received: %s', [x.Message]); 
    end); 

il generico TRequestHandler<T1,T2> è un DTO che avvolge il gestore insieme con i tipi TRequest e TResponse. Si eredita dal non generico TRequestHandler. Non sono sicuro che ci sia un modo migliore per farlo, ma era l'unico modo in cui potevo pensare di archiviare più tipi non correlati in una singola raccolta.

Tutto sembra funzionare correttamente. Il problema è quando viene ricevuto un messaggio di richiesta. Il codice C# per gestire i messaggi di richiesta simile alla seguente:

private void ProcessRequestTelegram(Telegram request) 
{ 
    var requestType = _RequestHandlers[request.MessageType].RequestType; 
    var typedRequest = JsonConvert.DeserializeObject(request.Payload, requestType); 
    var result = _RequestHandlers[request.MessageType].Handler.DynamicInvoke(typedRequest); 
    var jsonPayload = JsonConvert.SerializeObject(result); 
    var response = new Telegram() 
    { 
     Payload = jsonPayload, 
     CorrelationID = request.CorrelationID, 
     Template = TelegramTemplateEnum.Response, 
     SenderWindowHandle = _LocalWindowHandle.ToInt32(), 
     RecipientWindowHandle = _MessageRecipient.ToInt32(), 
     MessageType = request.MessageType 
    }; 
    var jsonResponse = JsonConvert.SerializeObject(response); 

    var resultCode = _Messaging.SendMessage(_MessageRecipient, jsonResponse); 
    CheckIfSendMessageErrorOccurred(resultCode); 
} 

Nel codice precedente RequestType è del tipo Type.

Ma per la vita di me non riesco a inventare l'equivalente Delphi. Ottengo fino al tentativo di deserializzare la richiesta. Scarica e sono bloccato su come passare il parser JSON del tipo per convertire il payload in. Ho provato vari modi di memorizzare TRequest e TResponse nelle proprietà RequestType e ResponseType di TRequestHandler: TTypeInfo, TRTTIType e attualmente TClass. Ma niente sembra darmi qualcosa di utile da passare a TJson.JsonToObject. Ha un sovraccarico generico che accetta un parametro di tipo per il tipo restituito ma apparentemente non è possibile utilizzare un parametro TClass come tipo.

+0

Non è affatto chiaro per me che i generici ti stanno facendo del bene qui. Questo sembra qualcosa in cui si desidera l'associazione di runtime piuttosto che la compilazione del tempo. –

+0

I generici sono lì principalmente per cui uno sviluppatore che progetta un nuovo messaggio non ha bisogno di ereditare da una classe specifica o implementare un'interfaccia specifica. Hanno solo bisogno di fornire una nuova classe e un gestore che sappia cosa farne.In ogni caso questa libreria è stata progettata da un collaboratore che utilizza C# e l'ho portato su Delphi. Ho tentato di mantenere l'API pubblica il più vicino possibile all'implementazione C#. –

+0

Non vedo davvero perché i generici siano necessari per questo. E tu sei costretto ad istanziare tutti i tipi generici in fase di compilazione. –

risposta

2

Non è possibile utilizzare TJson.JsonToObject<T> come al momento della chiamata di questo non si dispone di un T. generico. Guardare il codice di questo metodo e si vede che T viene passato a TJSONUnMarshal.CreateObject. Quindi nel tuo caso dovresti conservare lo TClass di TRequest e TResponse e scrivere la tua versione di TJson.JsonToObject che superi uno TClass.

Un altro modo sarebbe quello di creare prima l'istanza e poi passarla al non generico TJson.JsonToObject che prende lo ClassType dell'istanza passata e lo passa a TJSONUnMarshal.CreateObject.

+0

TJson è una classe Delphi stardard? Quale unità è in XE? – PSyLoCKe

Problemi correlati