Unboxing di tipo sconosciuto
-
03-10-2019 - |
Domanda
Sto cercando di capire sintassi che supporti unboxing di un tipo integrale (breve / int / lungo) per il suo tipo intrinseco, quando il tipo di per sé non è nota.
Ecco un esempio del tutto artificiosa che dimostra il concetto:
// Just a simple container that returns values as objects
struct DataStruct
{
public short ShortVale;
public int IntValue;
public long LongValue;
public object GetBoxedShortValue() { return ShortVale; }
public object GetBoxedIntValue() { return IntValue; }
public object GetBoxedLongValue() { return LongValue; }
}
static void Main( string[] args )
{
DataStruct data;
// Initialize data - any value will do
data.LongValue = data.IntValue = data.ShortVale = 42;
DataStruct newData;
// This works if you know the type you are expecting!
newData.ShortVale = (short)data.GetBoxedShortValue();
newData.IntValue = (int)data.GetBoxedIntValue();
newData.LongValue = (long)data.GetBoxedLongValue();
// But what about when you don't know?
newData.ShortVale = data.GetBoxedShortValue(); // error
newData.IntValue = data.GetBoxedIntValue(); // error
newData.LongValue = data.GetBoxedLongValue(); // error
}
In ogni caso, i tipi integrali sono coerenti, quindi non ci dovrebbe essere una qualche forma di sintassi che dice "l'oggetto contiene un semplice tipo di X, ritorno che come X (anche se non so che cosa è X)" . Poiché gli oggetti in ultima analisi, provengono dalla stessa fonte, in realtà non può essere una mancata corrispondenza (breve! = Lungo).
Mi scuso per l'esempio forzato, sembrava che il modo migliore per dimostrare la sintassi.
Grazie.
Soluzione
Bene, il object
in sé è il tipo più generica quadro sa. Se è un tipo di valore in scatola (compresi primitiva) o qualcos'altro non importa; Se si desidera ottenere maggiori specifico che sono di fare un typecast se non rimanete nel mondo "debolmente tipizzato" con object
(o, in C # 4, dynamic
).
Si noti, tuttavia, che è possibile utilizzare un elenco di condizioni per ottenere ciò che si vuole:
object boxedValue = GetBoxedValue();
if (typeof(short) == boxedValue.GetType()) {
newData.ShortValue = (short)boxedValue;
} else if (typeof(int) == boxedValue.GetType()) {
newData.IntValue = (int)boxedValue;
} else if (typeof(long) == boxedValue.GetType()) {
newData.LongValue = (long)boxedValue;
} else {
// not one of those
}
Modifica Una "scatola" generico può anche fare quello che vuoi:
public class Box<T>: IConvertible where T: struct, IConvertible {
public static implicit operator T(Box<T> boxed) {
return boxed.Value;
}
public static explicit operator Box<T>(T value) {
return new Box<T>(value);
}
private readonly T value;
public Box(T value) {
this.value = value;
}
public T Value {
get {
return value;
}
}
public override bool Equals(object obj) {
Box<T> boxed = obj as Box<T>;
if (boxed != null) {
return value.Equals(boxed.Value);
}
return value.Equals(obj);
}
public override int GetHashCode() {
return value.GetHashCode();
}
public override string ToString() {
return value.ToString();
}
bool IConvertible.ToBoolean(IFormatProvider provider) {
return value.ToBoolean(provider);
}
char IConvertible.ToChar(IFormatProvider provider) {
return value.ToChar(provider);
}
sbyte IConvertible.ToSByte(IFormatProvider provider) {
return value.ToSByte(provider);
}
byte IConvertible.ToByte(IFormatProvider provider) {
return value.ToByte(provider);
}
short IConvertible.ToInt16(IFormatProvider provider) {
return value.ToInt16(provider);
}
ushort IConvertible.ToUInt16(IFormatProvider provider) {
return value.ToUInt16(provider);
}
int IConvertible.ToInt32(IFormatProvider provider) {
return value.ToInt32(provider);
}
uint IConvertible.ToUInt32(IFormatProvider provider) {
return value.ToUInt32(provider);
}
long IConvertible.ToInt64(IFormatProvider provider) {
return value.ToInt64(provider);
}
ulong IConvertible.ToUInt64(IFormatProvider provider) {
return value.ToUInt64(provider);
}
float IConvertible.ToSingle(IFormatProvider provider) {
return value.ToSingle(provider);
}
double IConvertible.ToDouble(IFormatProvider provider) {
return value.ToDouble(provider);
}
decimal IConvertible.ToDecimal(IFormatProvider provider) {
return value.ToDecimal(provider);
}
DateTime IConvertible.ToDateTime(IFormatProvider provider) {
return value.ToDateTime(provider);
}
string IConvertible.ToString(IFormatProvider provider) {
return value.ToString(provider);
}
object IConvertible.ToType(Type conversionType, IFormatProvider provider) {
return value.ToType(conversionType, provider);
}
}
Questo può quindi essere usato al posto di object
; è ancora un riferimento all'oggetto ma è anche fortemente tipizzato alla struttura originale o tipo primitivo.
Altri suggerimenti
Non sono del tutto sicuro di quello che si desidera ottenere con questo, ma il tipo di DataStruct è errornous.
suppongo, non tutti i suoi metodi restituiscono LongValue.
struct DataStruct
{
public short ShortVale;
public int IntValue;
public long LongValue;
public object GetBoxedShortValue() { return ShortVale; }
public object GetBoxedIntValue() { return IntValue; }
public object GetBoxedLongValue() { return LongValue; }
}
In caso contrario, si può sempre utilizzare la classe Convert per cercare di convertire tra diversi tipi.
Ad esempio:
Convert.ToInt32(SomeObject);
Si prega di chiarire tuo post (basta premere il pulsante Modifica e modificarlo) se si intende qualcosa di diverso.
A proposito, conversione da object
può essere molto soggetto ad errori in quanto è il tipo di base di tutto. Così, un object
può essere qualsiasi cosa, e ciò significa che non si può sempre convertire in modo sicuro un object
ad un int o di qualsiasi altro tipo.
Altri esempi:
int value;
try
{
value = Convert.ToInt32(someObject);
}
catch (FormatException)
{
// the convertion is unsuccessful
}
E questo è anche utile:
int myValue;
if (!int.TryParse(something, out myValue))
{
//unsuccessful
}
Spero che questo aiuta.
È possibile tornare dynamic
, che possono poi essere gettato in un tipo integrale.
Come già detto da altri, il tuo esempio non funzionerà, perché ritorni LongValue da ogni metodo, quindi si otterrà un'eccezione getto valida qui (un boxed lungo non può essere lanciato a breve).
newData.ShortVale = (short)data.GetBoxedShortValue();
Tuttavia, utilizzando C # 4 di dynamic
, questo lavoro la volontà (notare le correzioni per i metodi e le dynamic
piuttosto che object
GetBoxed:
// Just a simple container that returns values as objects
struct DataStruct
{
public short ShortVale;
public int IntValue;
public long LongValue;
public dynamic GetBoxedShortValue() { return ShortValue; }
public dynamic GetBoxedIntValue() { return IntValue; }
public dynamic GetBoxedLongValue() { return LongValue; }
}
static void Main( string[] args )
{
DataStruct data;
// Initialize data - any value will do
data.LongValue = data.IntValue = data.ShortVale = 42;
DataStruct newData;
newData.ShortVale = (short)data.GetBoxedShortValue();
newData.IntValue = (int)data.GetBoxedIntValue();
newData.LongValue = (long)data.GetBoxedLongValue();
newData.ShortVale = data.GetBoxedShortValue(); // ok
newData.IntValue = data.GetBoxedIntValue(); // ok
newData.LongValue = data.GetBoxedLongValue(); // ok
}
Si noti che non è necessario alcun calchi in questi ultimi tre casi. Si noti anche, però, che se i tipi non si allineano, come in GetBoxedShortValue() { return LongValue; }
, le ultime tre righe si tradurrà in eccezioni del cast non validi. (È interessante notare che i primi tre non lo faranno, faranno solo il lavoro, ma quando si cambia di nuovo dynamic
in object
, che getterà le eccezioni del cast non valido.)