C#中关于引用类型做参数时的若干问题及与C/C++对比

1 篇文章 0 订阅
0 篇文章 0 订阅

     今天再次学习C#语言,发现这门语言与java是如此的相似(当然对于java我也是略懂皮毛偷笑),他们都有引用类型,但是C#中却还有引用类型的ref(引用的引用),这又让我想起了c/c++中指针的指针,它们之间有着怎样的关系呢?在这里我根据一些资料和自己的见解来结合例子做一做梳理。

     首先在java和C#里面有基本数据类型和引用数据类型,对于基本数据类型的ref和out这里不做解释,先看代码:

c#代码:

 class Test
    {
        public Test()
        {
            this.value = 0;
        }
        public Test(int x)
        {
            this.value = x;
        }
        public int value;
    }
    class Program
    {
        static void changeValue(Test test)
        {
            test.value = 1122;
        }
        static void changeValue_ref(ref Test test)
        {
            test.value = 2233;
        }
        static void Main(string[] args)
        {
            Test test = new Test();
            Console.WriteLine("[1]:{0}",test.value);
            changeValue(test);
            Console.WriteLine("[2]:{0}", test.value);
            changeValue_ref(ref test);
            Console.WriteLine("[3]:{0}", test.value);
            Console.ReadLine();
        }
    }
会发现结果是:


以上结果说明:引用变量test做参数时,是按引用传递的,可以理解为C语言中changeValue(Test *p),就像changeValue(Test test),test是个句柄,是个指针,虽然说java和C#表面没有指针(当然C#里面是有指针的),却处处隐藏着指针。

下面用一张图表示changeValue(Test test)方法里面处理过程:


然后讨论对 changeValue_ref(ref Test test)的理解,我认为这里的test就是一个别名,而且关系到引用我们就该想到别名,深刻体会“别名”的意味,其实就是C/C++中的**p(指针的指针)。那么在c++实现以上功能,就是:

#include <iostream>
#include <stdlib.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

class Test
    {
        public:
		 Test()
        {
            this->value = 0;
        }
         Test(int x)
        {
            this->value = x;
        }
        int value;
    };
 void changeValue(Test *test)
        {
            test->value = 1122;
        }
 void changeValue_ref(Test **test)
        {
            (*test)->value = 2233;
        }
int main(int argc, char** argv) {
	
	Test *test = new Test();
	cout<<"[0]"<<test->value<<endl;
	changeValue(test);
	cout<<"[1]"<<test->value<<endl;
	changeValue_ref(&test);
	cout<<"[2]"<<test->value<<endl;
        delete test;
        system("pause");
	return 0;
}

结果跟上面C#代码运行结果一样,这下注意到了其实C#和java中处处隐藏着指针了吧。

这样以后,我们开始讨论下一个问题:在changeValue(Test test)和changeValue_ref中new出新的内存,改变这个新的内存中的数据,结果会怎么样呢?如果真正明白了上面的内存分析图,我想不难做出正确推论。好,先看代码:

c#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CSharpRef
{
    class Test
    {
        public Test()
        {
            this.value = 0;
        }
        public Test(int x)
        {
            this.value = x;
        }
        public int value;
    }
    class Program
    {
        static void changeValue(Test test)
        {
            test = new Test();
            test.value = 1122;
        }
        static void changeValue_ref(ref Test test)
        {
            test = new Test();
            test.value = 2233;
        }
        static void Main(string[] args)
        {
            Test test = new Test();
            Console.WriteLine("[1]:{0}",test.value);
            changeValue(test);
            Console.WriteLine("[2]:{0}", test.value);
            changeValue_ref(ref test);
            Console.WriteLine("[3]:{0}", test.value);
            Console.ReadLine();
        }
    }
}
你猜到结果了吗?

如果你分析出错误结果请再仔细研究上面的内存图,特别注意的是内存图为何会分成两边(左右两部分),而对于ref(别名)就不会分成左右两部分,因为只有一个变量,别名还是代表自己,而不用ref时,就会有拷贝,只不过拷贝出的变量里存的是原变量所指的地址,细细品味!

你可能要问,在c++中呢?

#include <iostream>
#include <stdlib.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

class Test
    {
        public:
		 Test()
        {
            this->value = 0;
        }
         Test(int x)
        {
            this->value = x;
        }
        int value;
    };
 void changeValue(Test *test)
        {
        	test = new Test();
            test->value = 1122;
        }
 void changeValue_ref(Test **test)
        {
        	*test = new Test();
            (*test)->value = 2233;
        }
int main(int argc, char** argv) {
	
	Test *test = new Test();
	cout<<"[0]"<<test->value<<endl;
	changeValue(test);
	cout<<"[1]"<<test->value<<endl;
	changeValue_ref(&test);
	cout<<"[2]"<<test->value<<endl;
	delete test;
    system("pause");
	return 0;
}
结果如下:

一样的结果,可是后果却不一样,这段代码会产生内存泄露,关于c/c++内存泄露问题,这里不做进一步讨论。

c#中还有一个out关键字引起了我的注意,感觉它跟ref相似,但也是有区别的,MS那帮精明的老头是不会搞两个功能一样的关键字的,这里我们来讨论一下ref与out的区别:

已有前辈总结的比较好了,我来引用一下吧:

1、ref传进去的参数必须在调用前初始化,out不必,即:
int i;
SomeMethod( ref i );//语法错误
SomeMethod( out i );//通过

2、ref传进去的参数在函数内部可以直接使用,而out不可:
public void SomeMethod(ref int i)
{
   int j=i;//通过
   //...
}
public void SomeMethod(out int i)
{
   int j=i;//语法错误

}
3、ref传进去的参数在函数内部可以不被修改,但out必须在离开函数体前进行赋值。
ref在参数传递之前必须初始化;而out则在传递前不必初始化,且在 ... 值类型与引用类型之间的转换过程称为装箱与拆箱。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值