有关ref和out原理问题?

想问一下大佬们,ref和out是把值类型转化为引用类型了吗?按理来说值类型就是值类型,怎么会转为引用类型?能不能详细讲解一下!感谢!!!

封箱、拆箱
https://www.cnblogs.com/kdp0213/p/8533408.html

参数是什么类型还是什么类型,不会改变的
引用类型不用ref或out,都是在堆上,改变了数据就会影响原来的
而值类型不用的话就会重新分配一个地址,用上了就用原来的地址

不要太深究。
Object是万物的基类。
值类型和引用类型都派生自Object。
而Object自己是引用类型。
你说,那引用类型,怎么就生出一个值类型的小孩了呢?

对对,我绕进去了,可以通过装箱和拆箱操作进行转化,不过ref和out好像并没有进行装箱操作

大哥,我也不想深究,面试问这个啊

根据大佬们的解释以及我又查了资料,我重新理解为这样的:值类型其实也包含地址和值,不过看上去都是自身,平时变量间传递是值的传递,用ref/out修饰形参后传递给形参的是地址,C#在底层代码应该会判断各种情况并加以标识。我只能这样理解了

ref/out引用传递本身没有用到"封箱、拆箱"。
ref/out的dotnet的实现无非就是传递一个‘内存地址’。

比如有一个结构My
struct My
{
    public int a, b, c, d;
}
运行后一个My的实例1在内存里的布局将类似(17821540等数字,是内存地址):

   17821540:

--> 17821544:a

    17821548:b

    17821552:c

    17821556:d

一个int占用4个字节的内存,可以看到,b跟在a的后面,c跟在b的后面,d跟在c的后面,内存偏移都是4个字节。
如果我们把17821544传给某个过程,而该过程修改了内存17821548下的内容,实际上就等于修改实例1的b的值。
我们可以理解成17821544就是实例1的‘引用’,传递17821544的方式就是ref引用传递。

相形之下,按值传递则是拷贝了整个实例1的a,b,c,d的值,共4个int。按值传递后,过程对传入参数的修改,将修改到一个临时的内存拷贝下,不会影响到原来的实例1。

以下的示例代码需要设置项目属性/编译/允许unsafe:

unsafe class Program

{

    static void Main(string[] args)

    {

        My my = new My();

        Console.WriteLine("[ Main] address of my:   " new IntPtr(&my));

        Console.WriteLine("[ Main] address of my.b: " new IntPtr(&my.b));

        Console.WriteLine();

 

        ByRef(ref my);

        ByVal(my);

    }

    static void ByRef(ref My my)

    {

        fixed (void* a = &my)

        fixed (void* b = &my.b)

        {

            Console.WriteLine("[ByRef] address of my:   " new IntPtr(a));

            Console.WriteLine("[ByRef] address of my.b: " new IntPtr(b));

            Console.WriteLine();

        }

    }

    static void ByVal(My my)

    {

        void* a = &my;

        void* b = &my.b;

        {

            Console.WriteLine("[ByVal] address of my:   " new IntPtr(a));

            Console.WriteLine("[ByVal] address of my.b: " new IntPtr(b));

            Console.WriteLine();

        }

    }

    struct My

    {

        public int a, b, c, d;

    }

}

我的机器的运行结果为:

[ Main] address of my:   17821544

[ Main] address of my.b: 17821548

 

[ByRef] address of my:   17821544

[ByRef] address of my.b: 17821548

 

[ByVal] address of my:   17821432

[ByVal] address of my.b: 17821436

可以看到,在【ByRef】过程中,调试打印出的结构地址和【Main】调用方中结构地址是一样的,这就是按引用传递的实现。
可以看到,在【ByVal】过程中,调试打印出的结构地址和【Main】调用方中结构地址是不一样,因为ByVal过程中得到的是一个临时拷贝。



注:
这里可以理解成17821544就是实例1的‘引用’。但严格意义上它不是dotnet所谓的引用
资源下载.

一个int占用4个字节的内存,可以看到,b跟在a的后面,c跟在b的后面,d跟在c的后面,内存偏移都是4个字节。
如果我们把17821544传给某个过程,而该过程修改了内存17821548下的内容,实际上就等于修改实例1的b的值。
我们可以理解成17821544就是实例1的‘引用’,传递17821544的方式就是ref引用传递。

相形之下,按值传递则是拷贝了整个实例1的a,b,c,d的值,共4个int。按值传递后,过程对传入参数的修改,将修改到一个临时的内存拷贝下,不会影响到原来的实例1。

这也太本质了吧,赞!!

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 1024 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读