Unboxing to unknown type
Asked Answered
M

4

6

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.

Mcroberts answered 19/5, 2010 at 20:15 Comment(4)
All of you GetBoxed methods are returning LongValue. Typo?Hefter
What do you mean "there can't be a mismatch"? If you know the type, then there can't be; if you don't, then there can be.Springer
How would you want to use the result of your unboxing? If you don't know the type after unboxing, then you can't do anything with it (apart from guessing and using dynamic). If you do know the type after unboxing, then unbox to that type. In your example, you know that newData.IntValue can only be assigned with an int, so that you have to do newData.IntValue = (int)yourUnknownBoxedValue;. If this then fails, you can't assign it to newData.IntValue. If it doesn't fail, then you're fine. So what I'm saying is really: you should think up a not-contrived example, because this makes no sense.Isaacson
It's been a while since I posted this, but I believe the issue was a generalized routine which was dealing with LINQ-generated classes. I can't recall specifics, but I believe the key was being passed to the helper routine boxed inside an object; if the helped could simple "know" what is was being passed (short, int, or long), it could safely compare the keys with other keys from the same table. When the project dba decided to switch some of the keys to short, I was no longer able to use a single helper routine. Sorry, I can't recall specifics.Mcroberts
A
2

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.

Athalla answered 19/5, 2010 at 20:23 Comment(1)
As a side note, the Framework already contains a class remarkably similar to Box<T> (albeit without the conversion operators and IConvertible): StrongBox<T> (System.Runtime.CompilerServices).Fourfold
F
2

I'm not completely sure about what you'd like to achieve with this, but your DataStruct type is errornous.

I suppose, not all of its methods return 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; }
}

Otherwise, you can always use the Convert class to try to convert between different types.
For example:

Convert.ToInt32(SomeObject);

Please clarify your post (just hit the edit button and edit it) if you meant something different.

By the way, converting from object can be quite error-prone as it is the base type of everything. So, an object can be anything, and that means that you can't always safely convert an object to an int or any other type.

More examples:

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

And this is also useful:

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

I hope this helps.

Front answered 19/5, 2010 at 20:24 Comment(1)
@Hans, Convert.ToInt32() requires that you know the Int type at compile time -- this is exactly what I am trying to avoid.Mcroberts
S
1

You can return dynamic, which can then be cast to an integral type.

Springer answered 19/5, 2010 at 20:22 Comment(0)
F
0

As stated by others, your example won't work, because you return LongValue from each method, so you'll get an invalid cast exception here (a boxed long cannot be cast to short).

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

However, using C# 4's dynamic, this will work (note the fixes to your GetBoxed methods and dynamic rather than 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
}

Note that you don't need any casts in the last three cases. Note also, however, that if the types don't align, like in GetBoxedShortValue() { return LongValue; }, the last three lines will result in invalid cast exceptions. (Interestingly the first three won't, they'll just work, but when you change dynamic back into object, they will throw invalid cast exceptions.)

Fourfold answered 19/5, 2010 at 20:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.