浅谈值类型和引用类型在堆和栈中的存储二

前一篇我们浅谈了“堆”和“栈”,这篇文章我们主要谈一下值类型和引用类型在作为参数传递时候,有什么不同。

主要分为两种情况:

1.传递值类型(Passing Value Types)

2.传递引用类型(Passing Reference Types)

 

首先我们来看一下第一种情况,传递值类型(Passing Value Types)

复制代码
public void Go()
{
     var x = 5;
     AddFive(x);
     Console.WriteLine(x.ToString());
}

public int AddFive(int pValue)
{
     pValue += 5; 
     return pValue;
}
复制代码

上一篇文章我们已经讨论过关于值类型存储的问题,所以这里不再过多叙述。

我们都知道,值类型使用的就是数据本身,所以5会被拷贝到pValue。所以最终x的值还是不会改变。

需要谨记的是,如果我们将一个非常大的值类型变量(如一个很大的struct)复制到“栈“上,这对内存空间和处理器处理周期的消耗是很大的,因为”栈“空间并非无限的。

如何解决这种效率低的问题呢?先别慌,让我们来看一个例子。

复制代码
public void Go()
{
    MyStruct x = new MyStruct();
    DoSomething(ref x);
}

public struct MyStruct
{
    long a, b, c, d, e, f, g, h, i, j, k, l, m;
}

public void DoSomething(ref pValue)
{
    // DO SOMETHING HERE....
}
复制代码

看到没有,DoSomething()方法有一个ref关键字。这就引入了我们在传递值类型的另外一种情况,值类型按照引用传递。

看到没有,这个时候的pValue就是一个指向x的一个地址指针。

需要小心的是,当我们用引用的方法传递一个值参数时,我们访问的是源参数本身。任何对pValue的改变都是对x的改变。下面这段代码,会使x.a改变为12345。

复制代码
public void Go()
{
    MyStruct x = new MyStruct(); 
    x.a = 5;
    DoSomething(ref x);
    Console.WriteLine(x.a.ToString());
}

public voidDoSomething(ref MyStructpValue)
{
    pValue.a = 12345;
}
复制代码

 

 

 接下来我们来看看另外一种情况,传递引用类型(Passing Reference Types)

复制代码
public class MyInt
{
    public int MyValue;
}

public void Go()
{
    var x = new MyInt();
    x.MyValue = 2;
    DoSomething(x);
    Console.WriteLine(x.MyValue.ToString());
}

public void DoSomething(MyInt pValue)
{
    pValue.MyValue = 12345;
}
复制代码

最终会输出12345,发现结果已经被改变。将x的值(位于“堆“上的 MyInt 对象的地址)拷贝至pValue,使x和pValue同时指向MyInt对象的地址。

既然值类型可以使用ref关键字,那么引用类型在使用ref关键字的情况下,又是怎么一回事呢?好的,我们同样来看一个例子:

复制代码
public class Thing
{

}

public class Animal:Thing
{
    public int Weight;
}

public class Vegetable:Thing
{
    public int Length;
}

public void Go()
{
    Thing x = new Animal();
    Switcharoo(ref x);
    Console.WriteLine("x is Animal:"+ (x is Animal).ToString());
    Console.WriteLine("x is Vegetable:" + (x is Vegetable).ToString());
}

public void Switcharoo(ref Thing pValue)
{
    pValue = new Vegetable();
}
复制代码

最后程序执行的结果是:

x is Animal:False

x is Vegetable:True

pValue这次是指向x的,然后通过改变x的地址,使得pValue也发生改变。

 

 

好了,暂时就介绍到这里,如果有什么疑问,或者有误的地方,欢迎大家指点和交流。

posted @   Mind-Hacker  阅读(588)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示