Search code examples
c#.netobjectunboxingtype-inference

Unboxing to unknown type


I'm trying to figure out syntax that supports unboxing an integral type (short/int/long) to its intrinsic type, when the type itself is unknown.

Here is a completely contrived example that demonstrates the concept:

 // 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 each case, the integral types are consistent, so there should be some form of syntax that says "the object contains a simple type of X, return that as X (even though I don't know what X is)". Because the objects ultimately come from the same source, there really can't be a mismatch (short != long).

I apologize for the contrived example, it seemed like the best way to demonstrate the syntax.

Thanks.


Solution

  • Well, the object in itself is the most generic type the framework knows. Whether is is a boxed value type (including primitive) or something else doesn't matter; if you want to get more specific you have to do a typecast unless you remain in the "loosely typed" world with object (or, in C# 4, dynamic).

    Note, however, that you can use a list of conditions to achieve what you want:

    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: A generic "box" may also do what you want:

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

    This can then be used instead of object; it is still an object reference but it is also strongly typed to the original structure or primitive type.