2009-08-26 6 views
5

Come sostituire facilmente/rapidamente il float per il doppio (ad esempio) per la compilazione su due target diversi utilizzando queste due scelte particolari di tipi primitivi?Come sostituire temporaneamente un tipo primitivo con un altro durante la compilazione su target diversi?

Discussione: Ho una grande quantità di codice C# in fase di sviluppo che ho bisogno di compilare in alternativa utilizzare float, double o decimali a seconda del caso d'uso dell'assembly di destinazione.

Utilizzare qualcosa come "classe MYNumber: Double" in modo che sia solo necessario modificare una riga di codice non funziona come Double è sealed, e ovviamente non c'è #define in C#. Anche il peppering del codice con le istruzioni #if #else non è un'opzione, ci sono troppi operatori Math di supporto/codice relativo che usano questi particolari tipi primitivi.

Sono in perdita su come fare questo compito apparentemente semplice, grazie!

Edit: Solo un breve commento in relazione alla boxe menzionato in Kyles risposta: Purtroppo ho bisogno di evitare la boxe, soprattutto dal momento che vengono scelti del galleggiante quando è richiesta la massima velocità, e decimali, quando la massima precisione è la priorità (e prendendo il 20x + colpo di prestazione è accettabile). La boxe probabilmente escluderebbe i decimali come una scelta valida e in qualche modo sconfiggerà lo scopo.

Edit2: Per riferimento, quelli che suggeriscono i generici come una possibile risposta a questa domanda notano che ci sono molti problemi che contano i farmaci generici (almeno per i nostri bisogni). Per una panoramica e ulteriori riferimenti vedere Using generics for calculations

+0

+1 Bella domanda, qui sono state presentate molte soluzioni diverse, tutte molto simili, ma comunque interessanti. Potrei usarlo ora in futuro – LorenVS

risposta

8

Il modo migliore per farlo sarebbe utilizzando #if come Andrew detto sopra. Tuttavia, un'idea interessante per pensare sarebbe qualcosa di simile:

#if USE_FLOAT 
using Numeric = System.Single; 
#endif 

#if USE_DOUBLE 
using Numeric = System.Double; 
#endif 

#if USE_DECIMAL 
using Numeric = System.Decimal; 
#endif 

public class SomeClass 
{ 
    public Numeric MyValue{get;set;} 
} 

EDIT:

ho ancora piace molto questa soluzione, permette di fare alcune altre cose davvero interessanti come ad esempio:

Numeric.Parse(); 
Numeric.TryParse(); 

che funziona per tutti e tre i tipi

+0

Ho iniziato a pensare a una soluzione generica, poi l'ho vista. Cosi 'semplice. Duh. – x0n

+0

Sembra buono, molto semplice! Ci provo e torno da te ... – Keith

+0

Sì: l'unico vero svantaggio è che devi mettere questo costrutto in tutti i tuoi file; ma questo è sicuramente superato dal fatto che è possibile trattare le variabili dichiarate in questo modo come se si stessero usando i nomi dei tipi originali. +1 –

0

Nel peggiore dei casi penso che si potrebbe usare la boxe e qualcosa di simile:

#if USE_FLOAT 
public float OutputValue(object input) 
{ 
    return (float)input; 
} 
#endif 

#if USE_DOUBLE 
public double OutputValue(object input) 
{ 
    return (double)input; 
} 
#endif 

e chiamo OutputValue(1.5); per farlo per convertirlo per voi.

+0

Sfortunatamente ho bisogno di evitare il pugilato, soprattutto perché si scelgono i galleggianti quando è richiesta la massima velocità, e i decimali quando la massima precisione è la priorità e si accetta il colpo di performance 20x +. La boxe probabilmente escluderebbe i decimali come una scelta valida. Grazie per il suggerimento però - affronterò questo nella domanda principale. – Keith

0

Un approccio sarebbe avere un generico attorno al tipo di base necessario. Quindi dichiari una classe che eredita un'istanza specifica di quel generico in un file .cs separato e crei tre copie di questa classe per ciascun tipo di base di cui hai bisogno. Quindi modifichi il tuo .csproj per includere il file .cs corretto in base alla configurazione di build.

Nota che non ho ancora provato questo, quindi ci potrebbero essere un paio di nodi da appianare.

0

Si potrebbe fare questo:

public struct NumberContainer<T> 
{ 
    T _number; 

    // accessor, and possibly: operators, cast methods, etc. 
} 

E poi:

#if USE_FLOAT 
public struct MyNumber : NumberContainer<float> 
#else 
public struct MyNumber : NumberContainer<double> 
#endif 
{ 
} 
+0

Uuuh ... Non mi piace questa soluzione, trasforma quello che normalmente ti aspetteresti essere un tipo di valore in un tipo di riferimento. C'è una ragione per cui non hai creato NumberContains una struct? – LorenVS

+0

Inoltre, la catena di ereditarietà è unnecssary come si può usare: #if USE_FLOAT utilizzando MyNumber = NumberContains #endif e poi fare NumberContainer non astratta – LorenVS

+0

Siamo spiacenti, si, una struttura sarebbe meglio ... era pensando al costrutto principale piuttosto che ai dettagli. Modificato. –

0

Questo è difficile, in quanto i tipi di base non impelment qualcosa di simile IArithmetic.Questo è stato suggested many times on Connect. Sfortunatamente, questo è un well known limitation of generics.

Esistono alcuni accorgimenti, utilizzando uno "calculator" struct class. Se la matematica non è troppo onerosa, si lavora molto, molto bene.

Tuttavia, è goffo. Questo è un posto in cui i generici non sono flessibili come i modelli C++.

Un altro approccio potrebbe essere quello di utilizzare qualcosa come il Text Template Transformation Toolkit (T4) per generare un modello che possa funzionare con qualsiasi tipo e compilare quelli separati per tipo.