关于C#编程中引用与值类型赋值的一些容易犯错的地方

值类型与引用类型的区别在于:值类型在赋值的时候是拷贝值,引用类型在赋值的时候的拷贝引用。记住这一个原则,我们再来分析一些具体情况:

 1             PointStruct pt1 = new PointStruct(2,2);
 2             PointStruct pt2 = pt1;
 3             PointStruct[] ptsArray = new PointStruct[3];
 4             ptsArray[0] = pt1;
 5             ptsArray[1] = pt2;
 6             List<PointStruct> ptsList = new List<PointStruct>();
 7             ptsList.Add(pt1);
 8             ptsList.Add(pt2);
 9             List<PointStruct> pts2List = new List<PointStruct>();
10             pts2List.AddRange(ptsArray);
11
12             List<PointStruct> pts3List = pts2List;
13
14             Console.WriteLine("值类型的数组,原始值:");
15             Console.WriteLine(string.Format("pt1:x:{0},y:{1},pt2:x:{2},y:{3}", pt1.X, pt1.Y, pt2.X, pt2.Y));
16             Console.WriteLine(string.Format("ptsArray[0]:x:{0},y:{1},ptsArray[1]:x:{2},y:{3}",
17                 ptsArray[0].X, ptsArray[0].Y, ptsArray[1].X, ptsArray[1].Y));
18             Console.WriteLine(string.Format("ptsList[0]:x:{0},y:{1},ptsList[1]:x:{2},y:{3}",
19                 ptsList[0].X, ptsList[0].Y, ptsList[1].X, ptsList[1].Y));
20             Console.WriteLine(string.Format("pts2List[0]:x:{0},y:{1},pts2List[1]:x:{2},y:{3}",
21               pts2List[0].X, pts2List[0].Y, pts2List[1].X, pts2List[1].Y));
22             Console.WriteLine(string.Format("pts3List[0]:x:{0},y:{1},pts3List[1]:x:{2},y:{3}",
23              pts3List[0].X, pts3List[0].Y, pts3List[1].X, pts3List[1].Y));
24             pt2.X = 10;
25             pt1.Y = -1;
26             ptsArray[0].X = 100;
27             ptsArray[1].Y = 30;
28             ptsList[0] = new PointStruct(1000, 1000);
29             pts2List[1] = new PointStruct(-1000, -1000);
30             Console.WriteLine("值类型的数组,修改后:");
31             Console.WriteLine(string.Format("pt1:x:{0},y:{1},pt2:x:{2},y:{3}", pt1.X, pt1.Y, pt2.X, pt2.Y));
32             Console.WriteLine(string.Format("ptsArray[0]:x:{0},y:{1},ptsArray[1]:x:{2},y:{3}",
33                 ptsArray[0].X, ptsArray[0].Y, ptsArray[1].X, ptsArray[1].Y));
34             Console.WriteLine(string.Format("ptsList[0]:x:{0},y:{1},ptsList[1]:x:{2},y:{3}",
35             ptsList[0].X, ptsList[0].Y, ptsList[1].X, ptsList[1].Y));
36             Console.WriteLine(string.Format("pts2List[0]:x:{0},y:{1},pts2List[1]:x:{2},y:{3}",
37                pts2List[0].X, pts2List[0].Y, pts2List[1].X, pts2List[1].Y));
38             Console.WriteLine(string.Format("pts3List[0]:x:{0},y:{1},pts3List[1]:x:{2},y:{3}",
39            pts3List[0].X, pts3List[0].Y, pts3List[1].X, pts3List[1].Y));

其中PointStruct是一个值类型,我们先看结果:

从结果上可以看出来,值类型基本上都是各管各的,互不干扰,最后的pts2List与pts3List是同一个引用,所以修改其中任意一个是会影响另外一个的,数组,列表集合都会这样,因为其实就是同一个对象,只是取了2个名字而已,所以不管这个对象是否是值类型。

我们再看下面的代码:

 1             PointReference pt1 = new PointReference(2, 2);
 2             PointReference pt2 = pt1;
 3             PointReference[] ptsArray = new PointReference[3];
 4             ptsArray[0] = pt1;
 5             ptsArray[1] = pt2;
 6             List<PointReference> ptsList = new List<PointReference>();
 7             ptsList.Add(pt1);
 8             ptsList.Add(pt2);
 9             List<PointReference> pts2List = new List<PointReference>();
10             pts2List.AddRange(ptsArray);
11
12             List<PointReference> pts3List = pts2List;
13
14             Console.WriteLine("引用类型的数组,原始值:");
15             Console.WriteLine(string.Format("pt1:x:{0},y:{1},pt2:x:{2},y:{3}", pt1.X, pt1.Y, pt2.X, pt2.Y));
16             Console.WriteLine(string.Format("ptsArray[0]:x:{0},y:{1},ptsArray[1]:x:{2},y:{3}",
17                 ptsArray[0].X, ptsArray[0].Y, ptsArray[1].X, ptsArray[1].Y));
18             Console.WriteLine(string.Format("ptsList[0]:x:{0},y:{1},ptsList[1]:x:{2},y:{3}",
19                 ptsList[0].X, ptsList[0].Y, ptsList[1].X, ptsList[1].Y));
20             Console.WriteLine(string.Format("pts2List[0]:x:{0},y:{1},pts2List[1]:x:{2},y:{3}",
21               pts2List[0].X, pts2List[0].Y, pts2List[1].X, pts2List[1].Y));
22             Console.WriteLine(string.Format("pts3List[0]:x:{0},y:{1},pts3List[1]:x:{2},y:{3}",
23              pts3List[0].X, pts3List[0].Y, pts3List[1].X, pts3List[1].Y));
24             pt2.X = 10;
25             pt1.Y = -1;
26             ptsArray[0].X = 100;
27             ptsArray[1].Y = 30;
28             ptsList[0] = new PointReference(1000, 1000);
29             pts2List[1] = new PointReference(-1000, -1000);
30             Console.WriteLine("引用类型的数组,修改后:");
31             Console.WriteLine(string.Format("pt1:x:{0},y:{1},pt2:x:{2},y:{3}", pt1.X, pt1.Y, pt2.X, pt2.Y));
32             Console.WriteLine(string.Format("ptsArray[0]:x:{0},y:{1},ptsArray[1]:x:{2},y:{3}",
33                 ptsArray[0].X, ptsArray[0].Y, ptsArray[1].X, ptsArray[1].Y));
34             Console.WriteLine(string.Format("ptsList[0]:x:{0},y:{1},ptsList[1]:x:{2},y:{3}",
35              ptsList[0].X, ptsList[0].Y, ptsList[1].X, ptsList[1].Y));
36             Console.WriteLine(string.Format("pts2List[0]:x:{0},y:{1},pts2List[1]:x:{2},y:{3}",
37                pts2List[0].X, pts2List[0].Y, pts2List[1].X, pts2List[1].Y));
38             Console.WriteLine(string.Format("pts3List[0]:x:{0},y:{1},pts3List[1]:x:{2},y:{3}",
39            pts3List[0].X, pts3List[0].Y, pts3List[1].X, pts3List[1].Y));

其中PointReference是引用类型,先看看结果:

从结果上看pt1,pt2,ptsArray是相互影响的,因为PointReference是引用类型,另外ptsList是重新申请的一个类,只是添加了两个成员,成员是pt1,pt2,修改任意一个,也会影响这个ptsList,但是如果对ptsList的成员的X,Y是只读的,不能修改,所以只能重新赋值,在重新赋值后ptsList[0]都修改成1000,,1000了,但是其他的没有受影响,因为在修改以前里面存的引用是pt1,pt2的,在重新赋值后存的引用是重新new后的地址,自然不会对其他造成影响,然后pts2List也是一样的道理,修改pts2List是不会影响ptsList,但是会影响pts3List,因为他们的集合地址的引用是一样的,公用的存储空间。

我希望从这个例子中可以帮我彻底的疏通我对值类型与引用类型在运用中遇到的难题。

一些图示我就不画了,实际去分析我上传的例子就非常清楚了。

http://files.cnblogs.com/files/monkeyZhong/ReferenceTypeVSValueTypeDemo.zip

时间: 2024-10-31 05:22:18

关于C#编程中引用与值类型赋值的一些容易犯错的地方的相关文章

C#中,为什么在值类型后面加问号

在C#中,声明一个值类型或引用类型的变量,无论是否给这个变量赋初值,该变量都有默认值: 比如声明引用类型变量: string a,其等效于string a = null,string的默认值为null.比如声明值类型变量:int a, 其等效于int a = 0,int的默认值为0. 为什么需要让值类型可以为null? 来看需要让值类型为null的场景: 在数据库中,通常值类型允许为null,比如int, decimal, DateTime类型允许为null,而如果C#中对应的值类型不能为nul

Asp.net MVC 中Controller返回值类型ActionResult

内容转自 http://blog.csdn.net/pasic/article/details/7110134 Asp.net MVC中Controller返回值类型 在mvc中所有的controller类都必须使用"Controller"后缀来命名并且对Action也有一定的要求: 必须是一个public方法 必须是实例方法 没有标志NonActionAttribute特性的(NoAction) 不能被重载 必须返回ActionResult类型 如: [csharp] view pl

IOS 编程中引用第三方的方类库的方法及常见问题

方法一:直接复制所有源文件到项目中 这种方法就是把第三方类库的所有源文件复制到项目中,直接把所有.h和.m文件拖到XCode项目中即可. 注意: 1. 如果第三方类库引用了一些系统自带类库,那么在项目中还需要额外引用那些类库. 2. 如果当前的项目启用了ARC,而引用的第三方类库未使用ARC,那还需要在项目信息的Targets – Build Parses 里找到第三方类库的.m文件,并为它们加上-fno-objc-arc标记. 3. 对于在未启用ARC的项目用引用使用了ARC的第三方类库,则需

C#值类型赋值与引用类型的赋值

在C#中,数据类型大致可以分为两类,一类是值类型,一类是引用类型.初学者往往会被类型之间的相互赋值搞的很迷惑,尤其是引用类型变量的相互赋值. 现在举2个例子,来一一说明. 值类型变量的赋值: 值类型变量中保存的是实际数据,在赋值的时候只是把数据复制一份,然后赋给另一个变量. 例子1: int var1=2; int var2=var1;  //编译器会先复制var1的值,然后把它赋给var2.很明显var2的值也为2 引用类型变量的赋值:  引用类型变量中保存的是“指向实际数据的引用指针”.在进

C#中引用类型和值类型的区别,分别有哪些

C#的值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型. C#的引用类型包括:数组,用户定义的类.接口.委托,object,字符串. 数组的元素,不管是引用类型还是值类型,都存储在托管堆上. 引用类型在栈中存储一个引用,其实际的存储位置位于托管堆.为了方便,本文简称引用类型部署在托管推上. 值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储:作为局部变量时,存储在栈上. 值类型在内存管理方面具有更好的效率,并且不支持多态,适合用作存储数据的载体:引

在JavaScript中引用类型和值类型的区别

一.存储方式不一样 基本数据类型 变量存储的是简单的数据段,存储的是具体的值,是轻量级的数据存储方式 引用类型 引用类型的值,可以由多个值构成的对象,引用类型的变量存储的是对象引用地址.引用类型是重量的数据存储方式,分配在堆内存,频繁创建对象有损性能. 引用类型(N多) Object.Array.Date.Function.-- 二.动态属性不一样 1.引用类型能动态添加属性 var person=new Object(); person.name="jack"; person.age

IOS 编程中引用第三方的方类库的方法及常见问题(转载)

//原文:http://www.th7.cn/Program/IOS/201407/244585.shtml 方法一:直接复制所有源文件到项目中 这种方法就是把第三方类库的所有源文件复制到项目中,直接把所有.h和.m文件拖到XCode项目中即可. 注意: 1. 如果第三方类库引用了一些系统自带类库,那么在项目中还需要额外引用那些类库. 2. 如果当前的项目启用了ARC,而引用的第三方类库未使用ARC,那还需要在项目信息的Targets – Build Parses 里找到第三方类库的.m文件,并

PHP 编程中 10 个最常见的错误,你犯过几个?

错误1:foreach循环后留下悬挂指针 在foreach循环中,如果我们需要更改迭代的元素或是为了提高效率,运用引用是一个好办法: $arr = array(1,2,3,4); foreach($arr as&$value){    $value = $value *2; } // $arr is now array(2, 4, 6, 8) 这里有个问题很多人会迷糊. 错误1:foreach循环后留下悬挂指针 在foreach循环中,如果我们需要更改迭代的元素或是为了提高效率,运用引用是一个好

[C/C++]_[初级]_[编程容易犯错的地方]

场景: 1. 这里总结一些日常的容易犯错的细节. 问题1:一个类A有成员变量int deleted,给定一个A的对象指针 *a, 判断deleted为真的时候输出一个语句. 一般情况下新手会这样写: if(a) { if(a->deleted) { cout << "deleted" << endl; } } 但这样其实不够精简和浪费行数, 应该这样. if(a && a->deleted) { cout << "