惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
J
Java Code Geeks
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
美团技术团队
L
LangChain Blog
WordPress大学
WordPress大学
A
About on SuperTechFans
Martin Fowler
Martin Fowler
月光博客
月光博客
Y
Y Combinator Blog
U
Unit 42
D
Docker
Recent Announcements
Recent Announcements
Hugging Face - Blog
Hugging Face - Blog
B
Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
G
Google Developers Blog
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

博客园 - 宝气狗

【摄影】2008年06月07日西塘 【摄影】2008年05月24日大明山 2007年11月——感受黄山,天下无山。 将基础数据类型与字节数组相互转换 托管/非托管类型对照 【数据库】遍历XML根下的一级节点 - 宝气狗 - 博客园 Enterprise Library中缓存过期策略探究 css中的nowrap 2007年9月15日 九溪-五云山-梅家坞之行 宝气狗的新闻博开始使用http://webbased.cn [设计模式] 15.Command 命令模式 [设计模式] 22.State 状态模式 [设计模式] 23.Strategy 策略模式 CLR如何控制类型中字段的布局 MSIL指令速查 内存一致性问题(续一) 内存一致性问题 如何循序渐进向DotNet架构师发展 关于将Queue中的数据拼接成xml的经验
值类型装箱拆箱需要注意的地方
宝气狗 · 2007-07-12 · via 博客园 - 宝气狗

参考:
Applied Microsoft .NET Framework Programming by Jeffrey Richter
CLR Via C#,  Second Edition, by Jeffrey Richter

先回忆一下装箱和拆箱,看看下面这段代码有多少次装箱操作:
public static void Main()
{
    Int32 v = 5;
    Object o = v;//第一次,对v装箱,并将指针存放到o
    v = 123;
    //第二次,对v装箱,并将指针存放到堆栈等待Concat(拼接)操作
    //第三次,对o拆箱,并将拆箱后的值再次装箱等待Concat(拼接)
    Console.WriteLine(v + ", " + (Int32)o);
}
以上共进行了3次装箱。

下面的代码演示了值类型装箱易见的错误。
using System;
internal struct Point
{
    private Int32 _x, _y;
    public Point(Int32 x, Int32 y)
    {
        this._x = x;
        this._y = y;
    }

    public void Change(Int32 x, Int32 y)
    {
        this._x = x;
        this._y = y;
    }

    public override String ToString()
    {
        return String.Format("({0}, {1})", _x, _y);
    }
}
public sealed class Program
{
    public static void Main()
    {
        Point p = new Point(1, 1);
        Console.WriteLine(p);

        p.Change(2, 2);
        Console.WriteLine(p);

        Object o = p;
        Console.WriteLine(o);

        ((Point)o).Change(3, 3);
        Console.WriteLine(o);
    }
}

结果显示:
(1, 1)
(2, 2)
(2, 2)
(2, 2)

最后一行没达到预期的(3, 3)是因为o装箱后,产生的新Point实例调用了Change(3, 3),而并没有真正调用o的Change方法。

但可以使用接口来骗过c#从而达到对已装箱值类型进行更改的目的
using System;

internal interface IChangeBoxedPoint
{
    void Change(Int32 x, Int32 y);
}

internal struct Point : IChangeBoxedPoint
{
    private Int32 _x, _y;
    public Point(Int32 x, Int32 y)
    {
        this._x = x;
        this._y = y;
    }

    public void Change(Int32 x, Int32 y)
    {
        this._x = x;
        this._y = y;
    }

    public override String ToString()
    {
        return String.Format("({0}, {1})", _x, _y);
    }
}

public sealed class Program
{
    public static void Main()
    {
        Point p = new Point(1, 1);
        Console.WriteLine(p);

        p.Change(2, 2);
        Console.WriteLine(p);

        Object o = p;
        Console.WriteLine(o);

        ((Point)o).Change(3, 3);
        Console.WriteLine(o);

        ((IChangeBoxedPoint)p).Change(4, 4);
        Console.WriteLine(p);

        ((IChangeBoxedPoint)o).Change(5, 5);
        Console.WriteLine(o);

        Console.Read();
    }
}

结果为:
(1, 1)
(2, 2)
(2, 2)
(2, 2)
(2, 2)
(5, 5)

这里最后的结果是我们预期的,是因为o要转型为IChangeBoxedPoint,此时的o已经装箱。

所以值类型在进行装箱时要注意这点细节。
上面只是演示,所以采用了结构。其实Point用class写就可以避免上面的问题。