如何测试一个值是否在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);
这适用于任何类型,不仅适用于int
和object
把它放入可用的方法:
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不会显示盒装值和未装箱值之间的差异。
GetType
是System.Object
上的密封(非虚拟)方法。 在值类型上调用此方法肯定会将其打包 。 甚至Nullable
都GetType
这个问题 – 在可空的上调用GetType
会返回基础类型(如果它有一个值(作为底层类型))或者如果没有则抛出NullReferenceException
(盒装为null,可以不要引用空引用。
查看任何变量或值时,即使其类型为“动态”或“对象”,无论是否装箱或装箱。
通常,如果您有一个“保持”值类型的表达式,那么该表达式的值将是对框的引用, 除非表达式的编译时类型本身是值类型(generics稍微复杂一些) 。 可以保存对盒装结构的引用的公共引用类型是object
, dynamic
和接口类型。
与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
因此十进制值被加框。
var totalProperty = typeof(Report).GetProperty("Total"); var value = emit.DeclareLocal
*调用TryGetValue
**在If / Else中使用