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

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

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

任何建议?

 // 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"); 

请尝试以下方法

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

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

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

编辑

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

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

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

好吧,让我们用诀窍……

我们知道什么?

  • 当赋值为reference-type变量时,Value-type变量会一次又一次地被装箱
  • 引用类型变量不会再被装箱

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

isReferenceType在这里将是false ,因为我们比较堆上的两个对象(一个盒装在surelyBoxed ,一个盒装就在调用ReferenceEquals时):

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

isReferenceType在这里是真的 ,因为我们将堆上的1个对象与自身进行比较:

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

这适用于任何类型,不仅适用于intobject

把它放入可用的方法:

  public static bool IsReferenceType(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 

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

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

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

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

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

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

GetTypeSystem.Object上的密封(非虚拟)方法。 在值类型上调用此方法肯定会将其打包 。 甚至NullableGetType这个问题 – 在可空的上调用GetType会返回基础类型(如果它有一个值(作为底层类型))或者如果没有则抛出NullReferenceException (盒装为null,可以不要引用空引用。

查看任何变量或值时,即使其类型为“动态”或“对象”,无论是否装箱或装箱。

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

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

 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 item) { return (item != null) && (default(T) == null) && item.GetType().IsValueType; } public static bool IsBoxed(T? item) where T : struct { return false; } 

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

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

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

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

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

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

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

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

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

另一个微妙的点是value.GetType().IsValueType来自!typeof(T).IsValueType ,否则GetType()将创建值的临时盒装副本。

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

Sigil提供了一个梦幻般的UnBoxAny方法

假设您有以下内容:

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

new Dictionary { { "Total", 5m} }

因此十进制值被加框。

 var totalProperty = typeof(Report).GetProperty("Total"); var value = emit.DeclareLocal(); //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: 

*调用TryGetValue

**在If / Else中使用