如何测试值是否在C#/ .NET中装箱?

时间:2011-04-27 15:49:46

标签: c# .net dynamic boxing

我正在寻找一种编写代码来测试值是否已装箱的方法。

我的初步调查显示,.NET不顾一切地隐瞒事实,这意味着GetType()IsValueType不会揭示盒装值和未装箱值之间的差异。例如,在下面的LinqPad C#表达式中,我相信o1已装箱且i1未装箱,但我想用一种方法在代码中测试它,或者,第二,最好的方式在查看任何变量或值时,即使其类型为“动态”或“对象”,也可以知道 FOR SURE ,无论是盒装还是非盒装。

有什么建议吗?

// boxed? -- no way to tell from these answers!
object o1 = 123;
o1.GetType().Dump("o1.GetType()");
o1.GetType().IsValueType.Dump("o1.GetType().IsValueType");

// not boxed? -- no way to tell from these answers!
int i1 = 123;
i1.GetType().Dump("i1.GetType()");
i1.GetType().IsValueType.Dump("i1.GetType().IsValueType");

10 个答案:

答案 0 :(得分:33)

尝试以下

public static bool IsBoxed<T>(T value)
{
    return 
        (typeof(T).IsInterface || typeof(T) == typeof(object)) &&
        value != null &&
        value.GetType().IsValueType;
}

通过使用泛型,我们允许函数考虑编译器查看的表达式类型及其基础值。

Console.WriteLine(IsBoxed(42));  // False
Console.WriteLine(IsBoxed((object)42)); // True
Console.WriteLine(IsBoxed((IComparable)42));  // True

修改

有几个人要求澄清为什么这需要通用。并质疑为什么甚至需要它,开发人员不能只看代码并判断一个值是否装箱?为了尝试回答这两个问题,请考虑以下方法签名

void Example<T>(T param1, object param2, ISomething param3) where T : ISomething {
  object local1 = param1;
  ISomething local2 = param1;
  ...
}

在这种情况下,任何提供的参数或本地可能都可能代表盒装值,而且可能很容易。通过临时检查无法判断,只检查运行时类型和保持值的引用的组合可以确定。

答案 1 :(得分:6)

好吧,让我们使用这个技巧......

我们知道什么?

  • 值类型变量在分配到引用类型变量时反复加载
  • 参考类型变量不会被重新装箱 ......

所以我们只检查它是否再次被装箱(进入另一个对象)......所以我们比较参考

这里

isReferenceType false ,因为我们比较了堆上的2个对象(一个在surelyBoxed中装箱,一个装箱刚刚调用ReferenceEquals):

int checkedVariable = 123;     //any type of variable can be used here
object surelyBoxed = checkedVariable;
bool isReferenceType = object.ReferenceEquals(surelyBoxed, checkedVariable);
这里

isReferenceType true ,因为我们将堆上的1个对象与自身进行比较:

object checkedVariable = 123;     //any type of variable can be used here
object surelyBoxed = checkedVariable;
bool isReferenceType = object.ReferenceEquals(surelyBoxed, checkedVariable);

适用于任何类型,不仅适用于 int object

将其用于可用的方法:

    public static bool IsReferenceType<T>(T input)
    {
        object surelyBoxed = input;
        return object.ReferenceEquals(surelyBoxed, input);
    }

这种方法可以很容易地使用:

int i1 = 123;
object o1 = 123;
//...
bool i1Referential = IsReferenceType(i1);  //returns false
bool o1Referential = IsReferenceType(o1);  //returns true

答案 2 :(得分:4)

  

GetType()和IsValueType不显示   盒装值之间的差异   和未装箱的价值。

GetTypeSystem.Object上的密封(非虚拟)方法。在值类型上调用此方法将肯定框。甚至Nullable<T>都无法解决这个问题 - 在可空值上调用GetType将返回基础类型(如果它具有值(作为基础类型加框))或者抛出NullReferenceException如果它没有(装箱为空,不能取消引用空引用)。

  看着的时候   任何变量或值,即使它   类型是“动态”还是“对象”,是否   它是盒装或不盒装。

通常,如果您有一个“保持”值类型的表达式,则该表达式的值将是对框的引用,除非表达式的编译时类型具有该值-type本身(泛型稍微复杂一些)。可以保存对盒装结构的引用的公共引用类型是objectdynamic和接口类型。

答案 3 :(得分:4)

以下是一些简单的辅助方法,用于检查变量是否为盒装整数:

public static bool IsBoxed(object item)
{
    return true;
}

public static bool IsBoxed<T>(T item) where T : struct
{
    return false;
}

只需在变量上调用IsBoxed(...)

IsBoxed(o1) // evaluates to true
IsBoxed(i1) // evaluates to false

当然,这没有任何结果。为什么你需要知道一个值是否装箱?

答案 4 :(得分:2)

如果类型是值类型且其静态类型是“动态”或“对象”或接口,则总是装箱。

如果类型是值类型且其静态类型是实际类型,则永远不会装箱。

答案 5 :(得分:2)

Allon's answer类似,但应该为任何类型返回正确的答案,而不会产生编译时错误:

int i = 123;
Console.WriteLine(IsBoxed(i));    // false

object o = 123;
Console.WriteLine(IsBoxed(o));    // true

IComparable c = 123;
Console.WriteLine(IsBoxed(c));    // true

ValueType v = 123;
Console.WriteLine(IsBoxed(v));    // true

int? n1 = 123;
Console.WriteLine(IsBoxed(n1));    // false
int? n2 = null;
Console.WriteLine(IsBoxed(n2));    // false

string s1 = "foo";
Console.WriteLine(IsBoxed(s1));    // false
string s2 = null;
Console.WriteLine(IsBoxed(s2));    // false

// ...

public static bool IsBoxed<T>(T item)
{
    return (item != null) && (default(T) == null) && item.GetType().IsValueType;
}

public static bool IsBoxed<T>(T? item) where T : struct
{
    return false;
}

(虽然你可以认为由Allon的代码引起的可能的编译时错误是一个特性,而不是一个错误:如果你遇到编译时错误,那么你绝对不会处理一个未装箱的值类型! )

答案 6 :(得分:1)

我认为这个问题实际上是错误的。实际上不是问题,“我怎么能判断一个物体是否是另一种类型的盒子?”

参考Allon的评论,如果你有一个Object类型的对象而且该对象是一个原始值类型,那么它就是一个盒子。我不确定这是100%正确,但(类似于Allon的实现):

// Assume there is some object o.
bool isBoxed = o.GetType().IsPrimitive;

答案 7 :(得分:1)

这种方法类似于Jared Par的回答。但我认为!typeof(T).IsValueType比列举所有可能包含盒装值的类型更清晰。

public static bool IsBoxed<T>(T value)
{
    return !typeof(T).IsValueType && (value != null) && value.GetType().IsValueType;
}

与Jared的代码不同,这将处理T正确System.ValueType的情况。

另一个微妙之处是value.GetType().IsValueType位于!typeof(T).IsValueType之后,否则GetType()会创建一个临时的盒装副本。

答案 8 :(得分:1)

我不确定这是否与任何人相关,但是因为我遇到了这个帖子,因为拳击实际上正在影响我的动态映射。

Sigil提供了出色的UnBoxAny method

假设你有以下内容:

public class Report { public decimal Total { get; set; } }

new Dictionary<string, object> { { "Total", 5m} }

因此十进制值被装箱。

var totalProperty = typeof(Report).GetProperty("Total");

var value = emit.DeclareLocal<object>();

//invoke TryGetValue on dictionary to populate local 'value'*
                                                    //stack: [bool returned-TryGetValue]
//either Pop() or use in If/Else to consume value **
                                                    //stack: 
//load the Report instance to the top of the stack 
//(or create a new Report)
                                                    //stack: [report]
emit.LoadLocal(value);                              //stack: [report] [object value]
emit.UnboxAny(totalProperty.PropertyType);          //stack: [report] [decimal value]

//setter has signature "void (this Report, decimal)" 
//so it consumes two values off the stack and pushes nothing

emit.CallVirtual(totalProperty.SetMethod);          //stack: 

* invoke TryGetValue

** use in If/Else

答案 9 :(得分:-1)

试试这个:

    public static bool IsBoxed <T>(this T value) => 
       default(T) == null && value.GetType().IsValueType;