C#中的值类型和引用类型

栏目: ASP.NET · 发布时间: 5年前

内容简介:值类型和引用类型,是c#比较基础,但是也必须掌握的知识点,但是也不是那么轻易就能掌握,今天让我们一起来看看。首先我们看看这两种不同的类型有哪些比较典型的代表。int, long, float, double等原始类型中表示数字的类型都是值类型,表示时间的datatime也是值类型,除此之外我们还可以通过关键字struct自定义值类型。

前言

值类型和引用类型,是c#比较基础,但是也必须掌握的知识点,但是也不是那么轻易就能掌握,今天让我们一起来看看。

典型类型

首先我们看看这两种不同的类型有哪些比较典型的代表。

典型值类型

int, long, float, double等原始类型中表示数字的类型都是值类型,表示时间的datatime也是值类型,除此之外我们还可以通过关键字struct自定义值类型。

典型引用类型

原始类型中,array, list, dictionary, queue, stack和string都是引用类型,除此之外我们通过关键字class自定义引用类型。

基类

c#中所有的类型都最终继承自Object,这是没有疑问的,但是这其中还有些微区别。

值类型基类

对于值类型来说,除了最终继承自Object,还继承自ValueType,继承链如下

C#中的值类型和引用类型

但是请不要误解,这里仅仅指的是值类型天然是ValueType,但是不代表值类型能够这么声明

struct Struct1 : ValueType
{

}

这样是会引起编译错误的,值类型不能继承任何其他类型,值类型只能实现接口,不能继承自其它类型。只有引用类型既可以实现接口也能继承自其它类型。

引用类型基类

对于引用类型就没有那么麻烦,引用类型不会继承自ValueType。引用类型可以继承其他类型。

在内存中的表现

我们都知道,C#将内存分为了两部分,一个是Stack,另外一个是Managed Heap。一般来说,用于函数调用进栈,函数返回出栈,用的是Stack,而当创造一个新的实例时,会根据创建的实例属于值类型还是引用类型决定使用Stack还是Managed Heap。

值类型在内存中

当创建一个值类型对象时,c#会在Stack上面创建一块空间,这块空间就存放这个值类型对象。

int是一个典型的值类型,如下语句

int age = 10;

会存在于内存中的Stack上面。

C#中的值类型和引用类型

如果把值类型的实例赋值给另外一个值类型,那么效果就是复制一个新的值类型实例。

int myAge = age;

C#中的值类型和引用类型

引用类型在内存中

与值类型在内存中的表现不一样,创建一个引用类型的实例,不但会在Stack上面新建一个引用,还会在Heap上面划分出内存以容纳该引用类型实例。用户在使用的时候通过Stack上面的变量间接引用该实例。

class Author
{
    public string Name{get;set;}
    public int Age{get;set;}
}

Author author = new Author(){Name="deatharthas", Age= 32};

C#中的值类型和引用类型

注意看和值类型在内存中的区别,引用类型通过Stack上的变量访问位于Heap上面的实例。

在赋值的时候,拷贝的仅仅是Stack上面的变量,新拷贝出来的对象和旧的对象指向的是同一块内存。

Author myAuthor = author;

C#中的值类型和引用类型

这个时候,author和myAuthor指向同一块内存,称为同一性,通过调用

object.ReferenceEquals(myAuthor, author);

可以得到验证。

但可能有细心的朋友会有疑问了,不是说int是值类型,值类型是存在于Stack上面的吗?为什么在author类里面,它会在Heap里面呢?赞一个细心!值类型一般存在于Stack上面,但如果某个值类型包含于引用类型,那么它也会随着那个引用类型存放在Heap上面。

当参数时的行为区别

c#中的参数传递默认都是传值(by value),但是根据所传递对象是值类型还是引用类型,它们的行为还是有所区别,现在我们来看看。

值类型当参数

值类型当参数的时候,传递到函数内部的是一份值类型的拷贝,所以在函数内部修改这个拷贝不会影响原对象。

引用类型当参数

如果参数是引用类型,传递到函数内部的依然是一份拷贝,但是这个拷贝是其在Stack上面的变量的拷贝,就像上面的赋值那个例子。所以这个时候这份拷贝其实和原对象指向同一块内存(指向同一性),修改这个对象可以反映到原对象上面。

谨慎返回引用类型

编程是一项需要谨慎的工作,有时候我们经常会犯一些错误,而这些错误又是那么的不明显以至于不摔坑几次,我们根本察觉不了,考虑下面一个例子。

class People
    {
        public string Name { get; set; }
        public int Age { get; set; }
        private People _Father = null;
        public People Father { get { return _Father; } }
        public People(People father)
        {
            _Father = father;
        }
        public void ShowFather()
        {
            Console.WriteLine("father's name is " + Father.Name + " and his age is " + Father.Age);
        }
    }

    class Program
    {        
        static void Main(string[] args)
        {
            People father = new People(null) { Name = "father", Age = 60 };
            People son = new People(father);
            son.ShowFather();
            Console.ReadLine();
        }
    }

C#中的值类型和引用类型

看起来没什么问题,对吧?Father没有提供setter,似乎是安全的。但是我们试试下面的代码。

static void Main(string[] args)
        {
            People father = new People(null) { Name = "father", Age = 60 };
            People son = new People(father);
            var f = son.Father;
            f.Name="Changed";
            son.ShowFather();
            Console.ReadLine();
        }

C#中的值类型和引用类型

看,发现了什么,外部改变了本来应该被封装所保护的Father属性,封装被破坏了!

稍微一想我们应该能明白这个道理,Father属性返回的拷贝的变量和原Father变量指向同一块实例。要想解决这个问题,我们要么返回一个值类型,要么返回一个全新的对象。修改Father属性如下:

public People Father { get { return new People(_Father._Father) { Name = _Father.Name, Age = _Father.Age }; } }

再次测试,

C#中的值类型和引用类型

这次封装就没问题了。

总结

我们大概知道了值类型和引用类型的区别,包括它们的行为,在内存的居住方式,以及使用引用类型时可能会遇到的暗坑,希望大家通过阅读这篇文章,能够加深一些对它们的了解,少走一些弯路。

今天也简单的提到了比较时的同一性,和预防封装被破坏所采用的返回一个新的实例拷贝的策略(这个时候适合使用DeepCopy),我们之后会再详细聊它们。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web性能权威指南

Web性能权威指南

Ilya Grigorik / 李松峰 / 人民邮电出版社 / 2013-9 / 69

本书是谷歌公司高性能团队核心成员的权威之作,堪称实战经验与规范解读完美结合的产物。本书目标是涵盖Web 开发者技术体系中应该掌握的所有网络及性能优化知识。全书以性能优化为主线,从TCP、UDP 和TLS 协议讲起,解释了如何针对这几种协议和基础设施来优化应用。然后深入探讨了无线和移动网络的工作机制。最后,揭示了HTTP 协议的底层细节,同时详细介绍了HTTP 2.0、 XHR、SSE、WebSoc......一起来看看 《Web性能权威指南》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具