为什么我们需要在C#中装箱和拆箱?

本文翻译自:Why do we need boxing and unboxing in C#?

Why do we need boxing and unboxing in C#? 为什么我们需要在C#中装箱和拆箱?

I know what boxing and unboxing is, but I can't comprehend the real use of it. 我知道拳击和拆箱是什么,但我无法理解它的实际用途。 Why and where should I use it? 我应该在哪里以及在哪里使用它?

short s = 25;

object objshort = s;  //Boxing

short anothershort = (short)objshort;  //Unboxing

#1楼

参考:https://stackoom.com/question/8rOD/为什么我们需要在C-中装箱和拆箱


#2楼

In general, you typically will want to avoid boxing your value types. 通常,您通常希望避免装箱值类型。

However, there are rare occurances where this is useful. 但是,很少有这种情况有用。 If you need to target the 1.1 framework, for example, you will not have access to the generic collections. 例如,如果您需要定位1.1框架,则无法访问泛型集合。 Any use of the collections in .NET 1.1 would require treating your value type as a System.Object, which causes boxing/unboxing. 在.NET 1.1中使用集合需要将您的值类型视为System.Object,这会导致装箱/取消装箱。

There are still cases for this to be useful in .NET 2.0+. 仍有一些情况可以在.NET 2.0+中使用。 Any time you want to take advantage of the fact that all types, including value types, can be treated as an object directly, you may need to use boxing/unboxing. 只要您想利用所有类型(包括值类型)可以直接作为对象这一事实,您可能需要使用装箱/拆箱。 This can be handy at times, since it allows you to save any type in a collection (by using object instead of T in a generic collection), but in general, it is better to avoid this, as you're losing type safety. 这有时很方便,因为它允许您在集合中保存任何类型(通过在通用集合中使用对象而不是T),但一般来说,最好避免这种情况,因为您正在失去类型安全性。 The one case where boxing frequently occurs, though, is when you're using Reflection - many of the calls in reflection will require boxing/unboxing when working with value types, since the type is not known in advance. 但是,经常发生拳击的一种情况是,当你使用反射时 - 反射中的许多调用在处理值类型时需要装箱/拆箱,因为事先不知道类型。


#3楼

Boxing and Unboxing are specifically used to treat value-type objects as reference-type; Boxing和Unboxing专门用于将值类型对象视为引用类型; moving their actual value to the managed heap and accessing their value by reference. 将其实际值移动到托管堆并通过引用访问它们的值。

Without boxing and unboxing you could never pass value-types by reference; 没有装箱和拆箱,你永远不能通过引用传递值类型; and that means you could not pass value-types as instances of Object. 这意味着您无法将值类型作为Object的实例传递。


#4楼

Why 为什么

To have a unified type system and allow value types to have a completely different representation of their underlying data from the way that reference types represent their underlying data (eg, an int is just a bucket of thirty-two bits which is completely different than a reference type). 要有一个统一的类型系统,并允许值类型从引用类型表示其基础数据的方式中获得完全不同的基础数据表示(例如, int只是一个32位的桶,与a完全不同参考类型)。

Think of it like this. 想想这样。 You have a variable o of type object . 你有一个类型object的变量o And now you have an int and you want to put it into o . 现在你有一个int ,你想把它放进o o is a reference to something somewhere, and the int is emphatically not a reference to something somewhere (after all, it's just a number). o是对某个地方的引用,而int强调的不是对某个地方的引用(毕竟,它只是一个数字)。 So, what you do is this: you make a new object that can store the int and then you assign a reference to that object to o . 所以,你要做的是:创建一个可以存储int的新object ,然后将该对象的引用分配给o We call this process "boxing." 我们称这个过程为“拳击”。

So, if you don't care about having a unified type system (ie, reference types and value types have very different representations and you don't want a common way to "represent" the two) then you don't need boxing. 所以,如果你不关心拥有统一的类型系统(即,引用类型和值类型具有非常不同的表示,并且你不想要一种“代表”两者的常用方法),那么你不需要装箱。 If you don't care about having int represent their underlying value (ie, instead have int be reference types too and just store a reference to their underlying value) then you don't need boxing. 如果你不关心int表示它们的底层值(即, int也是引用类型而只是存储对它们的基础值的引用)那么你不需要装箱。

where should I use it. 我应该在哪里使用它。

For example, the old collection type ArrayList only eats object s. 例如,旧的集合类型ArrayList仅占用object s。 That is, it only stores references to somethings that live somewhere. 也就是说,它只存储对某些地方的某些东西的引用。 Without boxing you cannot put an int into such a collection. 没有拳击,你不能把int放入这样的集合。 But with boxing, you can. 但是对于拳击,你可以。

Now, in the days of generics you don't really need this and can generally go merrily along without thinking about the issue. 现在,在仿制药的时代,你真的不需要这个,并且通常可以快乐地走,而不考虑问题。 But there are a few caveats to be aware of: 但有一些需要注意的注意事项:

This is correct: 这是对的:

double e = 2.718281828459045;
int ee = (int)e;

This is not: 这不是:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception

Instead you must do this: 相反,你必须这样做:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;

First we have to explicitly unbox the double ( (double)o ) and then cast that to an int . 首先,我们必须显式地取消double(double)o ),然后将其转换为int

What is the result of the following: 以下是什么结果:

double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);

Think about it for a second before going on to the next sentence. 在继续下一句话之前,请考虑一下。

If you said True and False great! 如果你说True False Wait, what? 等等,什么? That's because == on reference types uses reference-equality which checks if the references are equal, not if the underlying values are equal. 那是因为引用类型上的==使用引用相等性来检查引用是否相等,而不是基础值是否相等。 This is a dangerously easy mistake to make. 这是一个非常容易犯的错误。 Perhaps even more subtle 也许更微妙

double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);

will also print False ! 也将打印False

Better to say: 更好地说:

Console.WriteLine(o1.Equals(o2));

which will then, thankfully, print True . 然后,谢天谢地,打印出True

One last subtlety: 最后一个微妙之处:

[struct|class] Point {
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);

What is the output? 什么是输出? It depends! 这取决于! If Point is a struct then the output is 1 but if Point is a class then the output is 2 ! 如果Point是一个struct那么输出是1但如果Point是一个class那么输出是2 A boxing conversion makes a copy of the value being boxed explaining the difference in behavior. 装箱转换使得装箱的值的副本解释了行为的差异。


#5楼

The last place I had to unbox something was when writing some code that retrieved some data from a database (I wasn't using LINQ to SQL , just plain old ADO.NET ): 我必须解开的最后一个地方是编写一些从数据库中检索一些数据的代码(我没有使用LINQ to SQL ,只是简单的旧ADO.NET ):

int myIntValue = (int)reader["MyIntValue"];

Basically, if you're working with older APIs before generics, you'll encounter boxing. 基本上,如果你在泛型之前使用旧的API,你会遇到拳击。 Other than that, it isn't that common. 除此之外,它并不常见。


#6楼

Boxing isn't really something that you use - it is something the runtime uses so that you can handle reference and value types in the same way when necessary. 拳击并不是你真正使用的东西 - 它是运行时使用的东西,因此你可以在必要时以相同的方式处理引用和值类型。 For example, if you used an ArrayList to hold a list of integers, the integers got boxed to fit in the object-type slots in the ArrayList. 例如,如果您使用ArrayList来保存整数列表,则整数将被装箱以适合ArrayList中的对象类型插槽。

Using generic collections now, this pretty much goes away. 现在使用通用集合,这几乎消失了。 If you create a List<int> , there is no boxing done - the List<int> can hold the integers directly. 如果创建List<int> ,则没有完成装箱 - List<int>可以直接保存整数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值