tags:

views:

119

answers:

5

I have a generic function that is constrained to struct. My inputs are boxed ("objects"). Is it possible to unbox the value at runtime to avoid having to check for each possible type and do the casts manually?

See the above example:

   public struct MyStruct
    {
        public int Value;
    }

    public void Foo<T>(T test)
        where T : struct
    {
        // do stuff
    }

    public void TestFunc()
    {
        object o = new MyStruct() { Value = 100 }; // o is always a value type

        Foo(o);
    }

In the example, I know that o must be a struct (however, it does not need to be MyStruct ...). Is there a way to call Foo without tons of boilerplate code to check for every possible struct type?

Thank you.

+2  A: 

No; you're using object, which is (by definition) not a struct/value type. Why are you intentionally boxing the value in this way?

Adam Robinson
All my values are stored in a Dictionary<string, object>, if I could have a Dictionary<string, struct> I would be glad to use that :) I am not "intentionally" boxing the value in my real application, this was just a simple example.
slurmomatic
@sluromatic: I can see the difficulty, then. Unfortunately, there is no way to do what you're trying to do without knowing the actual concrete value type.
Adam Robinson
@Adam: No, you can use a little reflection magic (see my answer below).
Johannes Rudolph
A: 

The whole point of using generics is to avoid situations like this.

When you actually "close" the generic with a type of struct, you eliminate the need for runtime type checking: ie.

Foo<MyStruct>(MyStruct test);

Your implementation of Foo, can safely assume that it's dealing with a struct.

Pierreten
Yes, but I don't know that is a MyStruct, I only know that it is a struct. It can be MyStruct2, MyStruct3, int, char, etc.
slurmomatic
Exactly, in those cases you'd make the call as Foo<MyStruct2>(MyStruct2 test) Foo<MyStruct3>(MyStruct3 test) etc... I'm not entirely sure why you'd use generics just to work around their main feature (design time type parameterization)
Pierreten
Ok, example. I have a generic class Value<T> but also an interface IValue (compare to List<T> and IList). I know that all my IValues are some concrete version of Value<T>. But in order to call any methods of Value<T> I have to cast to the concrete type, e.g. Value<int>, Value<string> etc.
slurmomatic
A: 

(Marked as CW because you can't pass an instance of ValueType to a generic requiring a struct, but it might be helpful for others who come across this question).


Instead of declaring o as an object, you can use a type of System.ValueType, which can only be assigned struct values; you cannot store an object in a ValueType.

However, I'm honestly not sure if that does anything in terms of (un)boxing. Note that ECMA-334 11.1.1 says:

System.ValueType is not itself a value-type. Rather, it is a class-type from which all value-types are automatically derived.

Mark Rushakoff
Any instance of `ValueType` is, ironically, a reference. This won't work for generics restricted to value types.
Adam Robinson
+1  A: 

.NET Generics are implemented in a manner that allows value types as a generic type parameter without incurring any boxing/unboxing overhead. Because your're casting to object before calling Foo you don't take advantage of that, in fact you're not even taking advantage of generics at all.

The whole point of using generics in the first place is to replace the "object-idiom". I think you're missing the concept here. Whatever type T happens to be, it is available at run-time and because you constrained it to struct guaranteed to be a struct type.

Your TestFunc could be written like this without problem:

public void TestFunc()
{
    MyStruct o = new MyStruct() { Value = 100 }; // o is always a value type

    Foo<MyStruct>(o);
}

Looking at Foo, it would look like this in your example:

public void Foo<T>(T test)
    where T : struct
{
    T copy = test; // T == MyStruct
}

EDIT:

Ok, since the OP clarified what he wants to call the generic method but doesn't know the type of his struct (it's just object). The easiest way to call your generic method with the correct type parameter is to use a little reflection.

public void TestFunc()
{
    object o = new DateTime();

    MethodInfo method = this.GetType().GetMethod("Foo");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(this, new object[] {o});


}
public void Foo<T>(T test)
    where T : struct
{
    T copy = test; // T == DateTime
}
Johannes Rudolph
But what if I don't know that o will be a MyStruct? It could by any struct or value type? TestFunc is only an example, in my application the data comes from a Dictionary<string, object> where the value can by anything. However, I know that it must be a struct and therefore I would want to call Foo without checking for the concrete type.
slurmomatic
But why do you need the concrete type? The point of using generics is to abstract away from the concrete type. Do you need to cast an object to the concrete type or what? Then you'd need to look into casting by example.
Johannes Rudolph
I do not need the concrete type, but since I can not cast to "struct" I need to know the concrete type to call Foo<MyConcreteType>().
slurmomatic
You can use reflection to do that (see above).
Johannes Rudolph
@Johannes: yeah, I thought about this, too. This will actually work like the OP wants. But is this a proper solution? I dont think so. There must be a better way, but since we dont know his intention/goal that is hard to tell...
Philip Daubmeier
Thank you! In my case this is a working solution. Not pretty, but if it saves me a lot of unnecessary code, it will do :)
slurmomatic
A: 

I dont know exactly what you are trying to archieve, but you could pass a delegate/lambda to unbox the value, and select some value in the struct you are interested in:

(Updated this code snippet after slurmomatics comment)

public void Foo<TValue>(object test, Func<object, TValue> ValueSelector)
           where TValue : struct
{
    TValue value = ValueSelector(test);

    // do stuff with 'value'
}

public void TestFunc()
{
    object o = new MyStruct() { Value = 100 };

    // Do the unboxing in the lambda.
    // Additionally you can also select some 
    // value, if you need to, like in this example
    Foo(o, x => ((MyStruct)x).Value);
}

Update:

Then do this:

public static void Foo<TUnboxed>(object test)
                     where TUnboxed : struct
{
    try
    {
        TUnboxed unboxed = (TUnboxed)test;
    }
    catch (InvalidCastException ex)
    {
        // handle the exception or re-throw it...
        throw ex;
    }

    // do stuff with 'unboxed'
}

public void TestFunc()
{
    // box an int
    object o = 100;

    // Now call foo, letting it unbox the int.
    // Note that the generic type can not be infered
    // but has to be explicitly given, and has to match the 
    // boxed type, or throws an `InvalidCastException`
    Foo<int>(o);
}
Philip Daubmeier
Hm, no. I still have the same problem that I have an object o, not a MyStruct o. I know that is has to be a struct at runtime but I do not know the concrete type at compile time.
slurmomatic
@slurmomatic: ok, updated my answer
Philip Daubmeier
Ok, thanks. The problem however remains. I don't know that is is an int (or MyStruct etc.), I only know that is a value type (struct).
slurmomatic
@slurmomatic: You dont know the type of the variable? Maybe you should rethink your design? Of course you could play around with the reflector, but is that really a proper solution? I dont think so.
Philip Daubmeier
@slurmomatic: BTW: what do you want to do in that generic function, what does 'do stuff' stand for?
Philip Daubmeier
I want to write a function that works with all structs. I know that I am providing a struct, I just don't know what concrete type (which may even be defined in a different library). My application is to upload a data array onto the GPU, the data may be any value type. I don't care which type.
slurmomatic
@slurmomatic: Ok, but what do you do with the struct then if you have unboxed it out of the object? I mean, you dont know what type the struct is of!? Sorry for the dumb questions, but I still dont get your intention. Maybe I could help you then.
Philip Daubmeier