C# 值类型和引用类型

C# 值类型和引用类型
概念上的直观区别:

  • 值类型:值类型直接存储其值
  • 引用类型:引用类型存储对值的引用
    存储位置的区别:
  • 值类型:堆栈
  • 引用类型:托管堆
    注意区分某个类型是值类型还是引用类型,因为这种存储位置的不同会有不同的影响。下面看两个例子:
i=20;
j=i;

int是值类型,这表示上面的语句会在内存的两个地方存储值20。

第二个例子,这段代码假定已经定义了一个类Vector,Vector是一个引用类型,它有一个int类型的成员变量Value:

Vector x,y;
x = new Vector();
x.Value = 30;
y = x;
Conso.WriteLine(y.Value); 
y.Value=50;
Conso.WriteLine(x.Value); 

要理解的重要一点是在执行这段代码后,只有一个Vector对象。x和 y都指向包含该对象的内存位置。因为x和 y是引用类型的变量,声明这两个变量只保留了一个引用,而不会实例化给定类型的对象。两种情况下都不会真正创建对象。要创建对象,就必须使用new关键字,如上所示。因为x和y引用同一个对象,所以对x的修改会影响y,反之亦然。因此上面的代码会显示30和50。
如果变量是一个引用,就可以把其值设置为null,表示它不引用任何对象:

y = null;

如果将引用设置为null,显然就不可能对它调用任何非静态的成员函数或字段,这么做会在运行期间抛出一个异常。

有点特别的 string 类型
string类型是C#预定义的引用类型。但是 , string与引用类型在常见的操作上有一些区别。例如,字符串是不可改变的。修改其中一个字符串 , 就会创建一个全新的string对象,而另一个字符串不发生任何变化。考虑下面的代码

namespace C1
{
    class Program
    {
       public static int Main()
       {
           string s1 = "a string";
           string s2 = s1;
           Console.WriteLine("s1:" + s1);
           Console.WriteLine("s2:" + s2);
           s1 = "another string";
           Console.WriteLine("s1 after:" + s1);
           Console.WriteLine("s2 after:" + s2);
           Console.ReadLine();
           return 0;  
       }
    }
}

其输出结果为:

s1:a string
s2:a string
s1 after:another string
s2 after:a string

改变s1的值对s2没有影响,这与我们期待的引用类型正好相反。当用值"a string"初始化s1时 , 就在堆上分配了一个新的string对象。在初始化s2时,引用也指向这个对象,所以s2的值也是"a string"。 但是当改变s1的值时,并不会替换原来的值,堆上会为新值分配一个新对象。s2变量仍指向原来的对象,所以它的值没有改变。这实际上是运算符重载的结果。

ref关键字

namespace C1
{
    class Program
    {
        static void SomeFunction(int[] inst, int i)
        {
            inst[0] = 100;
            i = 100;       
        }
        static void RefFunction(ref int i)
        {
            i = 80;
        }
        public static int Main()
        {
            int i = 0;
            int[] inst={0, 3, 4, 6, 2, 7, 8};
            ///显示初始值
            Console.WriteLine("i= " + i);                   //输出值为0
            Console.WriteLine("inst[0]= " + inst[0]);       //输出值为0

            ///调用SomeFunction()方法
            SomeFunction(inst,i);
            Console.WriteLine("i after= " + i);              //输出值为0
            Console.WriteLine("inst[0]= " + inst[0]);        //输出值为100 

            /*在变量通过引用传递给方法时,被调用的方法得到的就是这个变量,所以
             *在方法内部对变量进行的任何改变在方法退出后仍旧有效。而如果变量
             *通过值传送给方法,被调用的方法得到的是变量的一个相同副本,也就是
             *说,在方法退出后,对变量进行的修改会丢失。对于复杂的数据类型,按引
             *用传递的效率更高,因为在按值传递时,必须复制大量的数据 */

            //调用RefFunction()方法
            RefFunction(ref i);
            Console.WriteLine("i after= " + i);             //输出值为80
            Console.ReadLine();
            return 0;
            /*ref关键字使值参数通过引用传送给方法*/
        }
    }
}

注:学习内容来源于清华大学出版社的《C#高级编程》(第七版)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值