2010-10-22 17 views
5

L'obiettivo finale è specificare i contratti per una classe che risiede in un assembly esterno su cui non ho il controllo (ad esempio non posso aggiungere direttamente contratti a quella classe direttamente).Creazione di contratti di codice per una libreria legacy

Quello che ho provato finora:

  1. ContractClassFor attributo.
    Non funziona, perché la classe di destinazione deve fare riferimento alla classe del contratto.

  2. Creare manualmente un assembly di riferimento del contratto (ad esempio MyAsm.Contracts.dll) eseguendo il reverse engineering dei generatori automatici.
    Non funziona, perché subito dopo averlo compilato, il rewriter esegue il kicking e taglia via l'attributo ContractDeclarativeAssembly, il che rende l'assembly non riconoscibile dagli strumenti come "assembly di riferimento del contratto". E non riesco a trovare un modo per disattivare il rewriter.

  3. Creare un assembly "falso" con lo stesso nome, versione, nome sicuro (se presente) e altri attributi dell'assembly di destinazione e inserire le classi con lo stesso nome con metodi con lo stesso nome. Quindi compilare l'assembly con l'opzione "build contract reference" attivata, quindi prendere l'assembly di riferimento del contratto generato e utilizzarlo.
    Anche questo non ha funzionato per qualche motivo, anche se non ho idea di quale fosse esattamente questa ragione. Il controllo statico semplicemente ignora il mio assembly di riferimento generato da smartypants, come se non esistesse.

E un'altra cosa, nel caso in cui è importante: l'assieme di destinazione è compilato per .NET 2.0, e non posso ricompilare per 4,0.

Aggiornamento

Scrivi libreria "wrapper" con contratti definiti è fuori discussione.

Per uno, è un sacco di codice in più da scrivere. Ma anche se lo si lascia da parte, potrebbe essere una penalità significativa per le prestazioni: dovrei (potenzialmente) creare classi wrapper per ogni tipo "legacy" che viene usato come tipo di ritorno dai miei metodi "legacy". Ma anche quella non è la fine della storia. Immagina che alcuni metodi possano restituire riferimenti alle interfacce di base, e quindi il codice chiamante potrebbe lanciarli su interfacce derivate per vedere se l'oggetto supporta più funzionalità. Come li avvolgo? Probabilmente dovrei fornire metodi personalizzati Cast<T>() sulle mie classi di wrapping, che proveranno a eseguire il cast della classe/interfaccia sottostante e restituiranno un wrapper per il risultato se avrà successo. Alla fine, la mia intera base di codice diventerà così complicata che probabilmente non ne valga la pena.

D'altra parte, proprio questo problema è già stato risolto con successo dal team CC stesso: spediscono gli assembly di riferimento del contratto appropriati per mscorlib, System.Core e alcuni altri gruppi di sistema, no? Come sono riusciti a costruirli? Se possono farlo, allora non vedo alcuna ragione per cui non dovrei essere in grado di realizzare lo stesso trucco.

Oh, beh, in realtà, vedo una ragione: non so come farlo. :-) - Fyodor Soikin 18 sec fa modifica

+0

Puoi riflettere l'altro assieme e solo decompilarlo? –

+0

Non ho bisogno di decompilarlo, in realtà ho il codice sorgente per questo. Ma il punto non è modificare quel codice. È anche usato in altri progetti, che hanno come target 2.0, quindi anche il più piccolo cambiamento possibile nell'aggiunta degli attributi '[ContractClass]' spezzerebbe quei progetti. Inoltre, non voglio separare il codice sorgente, perché in tal caso mi piacerebbe davvero unire le future modifiche in quel ramo. –

+0

Beh, se si mantiene la sorgente da soli, sarebbe molto facile mantenere un ramo 2.0 e 4.0 dei contratti se si utilizza git/mercurial dove il ramo 2.0 è predefinito e il 4.0 è un ramo di funzionalità, quando vengono aggiunte le proprietà/cambiato al ramo predefinito dopo che tutto ciò che devi fare è usare il DVCS per unire le modifiche nel ramo 4.0 allora. Questo sarebbe molto poco lavoro a patto che ci si assicuri che l'unico codice che si modifica nel ramo 4.0 sia correlato al codice dei contratti C# e non alle stesse classi. –

risposta

4

Probabilmente sei stato quasi lì con (2). Assicurati di disattivare la riscrittura del contratto per l'assembly C# che costruisci a mano. Disattiva tutti i controlli di run-time e l'analisi statica e il re-writer non deve eseguire il kick-in. Mettilo dove sono in corso tutti gli altri assembly CodeContracts, ad es. per /bin/X.dll, creare un assembly che vada a /bin/CodeContracts/X.Contracts.dll.

Vedere http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/5fe7ad4e-d4f1-4bb5-806e-a1e31f550181. Avevi ragione: basta creare tutti i bit giusti guardando Reflector e funziona. Volevo farlo per aggiungere contratti all'assembly F # finché F # non è in grado di gestire i contratti.

+0

Non sono davvero sicuro di come sia successo, ma in qualche modo sono riuscito a mancare la casella di controllo che spegne il rewriter . E ora che ho guardato la seconda volta e l'ho trovato, tutto funziona alla grande. Grazie mille! –

1

Una soluzione potrebbe essere quella di creare uno strato di "facciata" tra il codice e l'assieme esterno. Questo livello può contenere tutti i contratti che applicheresti sul codice esterno.

Se non si desidera modificare nulla nel codice esistente, temo che non ci sia molto altro da fare.

+0

Beh, sì, questo è fondamentalmente quello che sto facendo in questo momento. Per uno, è un sacco di codice in più da scrivere. Ma anche se lo si lascia da parte, potrebbe essere una penalità significativa per le prestazioni: dovrei (potenzialmente) creare classi wrapper per ogni tipo "legacy" che viene usato come tipo di ritorno dai miei metodi "legacy". Ma anche quella non è la fine della storia. Immagina che alcuni metodi possano restituire riferimenti alle interfacce di base, e quindi il codice chiamante potrebbe lanciarli su interfacce derivate per vedere se l'oggetto supporta più funzionalità. Come li avvolgo? Avvolgere un'intera libreria è un grande sforzo. –

+0

D'altra parte, proprio questo problema è già stato risolto con successo dal team CC stesso: spediscono gli assembly di riferimento del contratto appropriati per mscorlib, System.Core e alcuni altri gruppi di sistema, no? Come sono riusciti a costruirli? Se possono farlo, allora non vedo alcuna ragione per cui non dovrei essere in grado di realizzare lo stesso trucco. Oh, beh, in realtà, vedo una ragione: non so come farlo. :-) –

+2

Vedendo che la classe Contract fa parte del BCL, suppongo che abbiano accesso al codice mscorlib =) – koenmetsu

Problemi correlati