我正在寻找一种编写代码来测试值是否已装箱的方法。
我的初步调查显示,.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");
答案 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不显示 盒装值之间的差异 和未装箱的价值。
GetType
是System.Object
上的密封(非虚拟)方法。在值类型上调用此方法将肯定框。甚至Nullable<T>
都无法解决这个问题 - 在可空值上调用GetType
将返回基础类型(如果它具有值(作为基础类型加框))或者抛出NullReferenceException
如果它没有(装箱为空,不能取消引用空引用)。
看着的时候 任何变量或值,即使它 类型是“动态”还是“对象”,是否 它是盒装或不盒装。
通常,如果您有一个“保持”值类型的表达式,则该表达式的值将是对框的引用,除非表达式的编译时类型具有该值-type本身(泛型稍微复杂一些)。可以保存对盒装结构的引用的公共引用类型是object
,dynamic
和接口类型。
答案 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:
答案 9 :(得分:-1)
试试这个:
public static bool IsBoxed <T>(this T value) =>
default(T) == null && value.GetType().IsValueType;