盒装值类型的通用拆箱

时间:2010-05-03 16:45:07

标签: c# .net

我有一个限制为struct的泛型函数。我的输入是盒装(“对象”)。是否可以在运行时取消装箱值以避免检查每种可能的类型并手动执行转换?

参见上面的例子:

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

在这个例子中,我知道o必须是一个结构(但是,它不需要是MyStruct ......)。有没有办法在没有大量样板代码的情况下调用Foo来检查每种可能的结构类型?

谢谢。

5 个答案:

答案 0 :(得分:8)

.NET Generics的实现方式允许将值类型作为泛型类型参数,而不会产生任何装箱/拆箱开销。因为你在调用Foo之前​​投射到对象你没有利用它,事实上你甚至根本没有利用泛型。

首先使用泛型的全部意义是替换“对象习语”。我想你在这里错过了这个概念。 无论类型T是什么类型,它都可以在运行时使用,因为您将其约束为struct保证为结构类型。

你的TestFunc可以这样编写而没有问题:

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

    Foo<MyStruct>(o);
}

看看Foo,在你的例子中看起来像这样:

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

编辑:

好的,因为OP澄清了他想要调用泛型方法的内容,但不知道他的结构类型(它只是对象)。使用正确的类型参数调用泛型方法的最简单方法是使用一点反射。

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
}

答案 1 :(得分:2)

没有;您正在使用object,这是(根据定义)不是结构/值类型。你为什么故意以这种方式装箱?

答案 2 :(得分:0)

使用泛型的全部意义在于避免这种情况。

当你用一种结构实际“关闭”泛型时,你不需要运行时类型检查:即。

Foo<MyStruct>(MyStruct test);

您对Foo的实现可以安全地假设它正在处理结构。

答案 3 :(得分:0)

(标记为CW,因为您无法将ValueType的实例传递给需要struct的通用,但对于遇到此问题的其他人可能会有帮助。)< / em>的


您可以使用System.ValueType类型,而不是将o声明为object,而只能为struct分配值object;您无法在ValueType中存储System.ValueType

但是,老实说,我不确定这是否会对(联合国)拳击做任何事情。请注意ECMA-334 11.1.1说:

  

{{1}}本身不是值类型。相反,它是一个类类型,从中自动派生所有值类型。

答案 4 :(得分:0)

我不确切知道你想要什么,但你可以传递一个委托/ lambda来取消该值,并在你感兴趣的结构中选择一些值:

(在slurmomatics评论后更新了此代码段)

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

<强>更新

然后这样做:

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