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

Reb*_*bin 25 .net c# boxing dynamic

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

我的初步调查表明,.NET不顾一切地隐瞒事实,这意味着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");
Run Code Online (Sandbox Code Playgroud)

Jar*_*Par 33

请尝试以下方法

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

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

Console.WriteLine(IsBoxed(42));  // False
Console.WriteLine(IsBoxed((object)42)); // True
Console.WriteLine(IsBoxed((IComparable)42));  // True
Run Code Online (Sandbox Code Playgroud)

编辑

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

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

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

  • @ Random832实际上如果你的约束条件决定它是一个合理的立场.性能问题和拳击对性能有直接影响. (5认同)
  • @Allon:假设我是一个图书馆作家,并且无法访问可能使用我的库的所有代码.在Debug版本中,我希望通过将盒装值传递给我的库例程来告知用户他们正在产生一个性能损失.我没有看到在我的库中使用静态检查的方法. (4认同)
  • @Allon因为知道一个值是否装箱,你需要运行时和编译时信息.在处理嵌套泛型时,这一点尤为重要 (3认同)
  • @Allon,总的来说我不认为它特别有用.但这是一个有效的问题,所以我试图提供最好的答案.我可以看到它是如何在一个**非常**性能敏感的应用程序中的`Debug.Assert`检查它是有价值的,以确保它不会浪费带有盒装值的内存 (3认同)
  • 顺便说一下,我认为更好的实现是`return!typeof(T).IsValueType && value!= null && value.GetType().IsValueType;`它处理nll-references和`T = System.ValueType`的情况. (2认同)
  • "在一个你不想处理盒装值的API中使用支架是完全合理的"不,不是.如果您愿意处理参考类型,请不要这样做.与直接处理价值类型相比,拳击只是一种性能损失. (2认同)

Mar*_*oth 6

好吧,让我们用诀窍......

我们知道什么?

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

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

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

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

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

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

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

把它放入很好用的方法:

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

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

int i1 = 123;
object o1 = 123;
//...
bool i1Referential = IsReferenceType(i1);  //returns false
bool o1Referential = IsReferenceType(o1);  //returns true
Run Code Online (Sandbox Code Playgroud)

  • 我认为您有一个很好的基本想法,但是有一些错误:ReferenceEquals()方法已经接受了对象,因此如果T为值类型,则两个输入都将“确定地装箱”,反之,如果T不是一个值,则将它们都装箱。因此,您唯一需要的是“公共静态布尔IsReferenceType &lt;T&gt;(T input){返回对象。ReferenceEquals(input,input); }`(因为如果将每个参数都装箱,则它们将单独装箱-即使它们是相同的变量)。 (3认同)