2010-05-19 8 views
6

Próbuję znaleźć składnię, która obsługuje unboxing typu integralnego (short/int/long) do jego typu wewnętrznego, gdy sam typ jest nieznany.Unboxing to unknown type

Tutaj jest całkowicie wymyślony przykład, który pokazuje koncepcję:

// 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 
} 

W każdym przypadku, integralne typy są zgodne, więc nie powinno być jakaś forma składni, które mówi, że”obiekt zawiera prosty typ X, zwróć to jako X (nawet jeśli nie wiem, co to X) ". Ponieważ obiekty ostatecznie pochodzą z tego samego źródła, tak naprawdę nie może być niezgodności (short! = Long).

Przepraszam za wymyślony przykład, wydawało się najlepszym sposobem na zademonstrowanie składni.

Dzięki.

+0

Wszystkie metody 'GetBoxed' zwracają wartość" LongValue ". Typo? – unholysampler

+0

Co masz na myśli "nie może być niedopasowania"? Jeśli znasz typ, to nie może być; jeśli nie, to może być. –

+1

Jak chcesz wykorzystać wynik rozpakowania? Jeśli nie znasz typu po rozpakowaniu, nie możesz nic z nim zrobić (poza zgadywaniem i używaniem 'dynamic'). Jeśli znasz typ po rozpakowaniu, odinstaluj go do tego typu. W twoim przykładzie wiesz, że newData.IntValue może być przypisany tylko z int, więc ty masz _ do zrobienia 'newData.IntValue = (int) yourNastenBoxedValue;'. Jeśli to się nie powiedzie, nie możesz przypisać go do newData.IntValue. Jeśli to się nie uda, to nic ci nie jest. To, co mówię, jest naprawdę: powinieneś wymyślić nie wymyślony przykład, ponieważ to nie ma sensu. – Joren

Odpowiedz

2

Cóż, sam w sobie jest najbardziej typowym typem architektury. Niezależnie od tego, czy jest to typ wartości pudełkowej (w tym prymitywny), czy też coś innego, nie ma znaczenia; Jeśli chcesz uzyskać bardziej szczegółowe informacje, musisz mieć, aby zrobić typografię, chyba że pozostaniesz w świecie "luźno wpisanym" z object (lub, w C# 4, dynamic).

Należy jednak pamiętać, że można użyć listę warunków, aby osiągnąć to, co chcesz:

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 
} 

Edit: rodzajowy "box" może też robić to, co chcesz:

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); 
    } 
} 

To może być następnie użyte zamiast object; nadal jest referencją do obiektu, ale jest również silnie wpisany do pierwotnej struktury lub typu pierwotnego.

+0

Na marginesie, Framework zawiera już klasę bardzo podobną do "Box " (choć bez operatorów konwersji i IConvertible): 'StrongBox ' (System.Runtime.CompilerServices). – Ruben

1

Możesz zwrócić dynamic, który następnie można rzucić na typ integralny.

2

Nie jestem do końca pewien, co chciałbyś osiągnąć, ale twój typ DataStruct jest nieuchwytny.

Przypuszczam, że nie wszystkie jego metody zwracają 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; } 
} 

W przeciwnym razie zawsze można użyć klasy Konwertuj, aby spróbować przekonwertować między różnymi typami.
Na przykład:

Convert.ToInt32(SomeObject); 

proszę wyjaśnić swoje stanowisko (wystarczy nacisnąć przycisk edycji i edytować) jeśli masz na myśli coś innego.

Nawiasem mówiąc, konwersja z object może być dość podatna na błędy, ponieważ jest podstawowym typem wszystkiego. Tak więc, object może być dowolny, a to oznacza, że ​​nie zawsze można bezpiecznie przekonwertować object na int lub dowolny inny typ.

Więcej przykładów:

int value; 
try 
{ 
    value = Convert.ToInt32(someObject); 
} 
catch (FormatException) 
{ 
    // the convertion is unsuccessful 
} 

i to jest również użyteczne:

int myValue; 
if (!int.TryParse(something, out myValue)) 
{ 
    //unsuccessful 
} 

Mam nadzieję, że to pomaga.

+0

@Hans, Convert.ToInt32() wymaga znajomości typu Int w czasie kompilacji - jest to * dokładnie * to, czego staram się uniknąć. –

0

Jak twierdzą inni, twój przykład nie zadziała, ponieważ zwrócisz LongValue z każdej metody, więc otrzymasz tutaj wyjątek odrzutów (nie można rzucić krótkiego pudełka o długiej ramie).

newData.ShortVale = (short)data.GetBoxedShortValue(); 

Jednak przy użyciu C# 4 na dynamic, to będzie działać (nota poprawki do GetBoxed metod i dynamic zamiast object:

// 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 
} 

Należy pamiętać, że nie trzeba żadnych odlewane w ostatnim Zwróć jednak uwagę, że jeśli typy nie są wyrównane, tak jak w przypadku GetBoxedShortValue() { return LongValue; }, ostatnie trzy linie spowodują nieważne wyjątki dotyczące rzutowania. (Co ciekawe, pierwsze trzy nie będą działać, ale po zmień dynamic z powrotem na object, oni spowoduje odrzucenie nieprawidłowych rzutów.)