C# 之String以及浅拷贝与深拷贝
一、String到底是值类型还是引用类型
MSDN 中明确指出 String 是引用类型而不是值类型,但 String 表面上用起来却像是值类型,这又是什么原因呢?
首先从下面这个例子入手:
//值类型
int a = ;
int b = a;
a = ;
Console.WriteLine("a is {0},b is {1}", a, b); //引用类型
string str1 = "ab";
string str2 = str1;
str1 = "abc";
Console.WriteLine("str1 is {0},str2 is {1}", str1, str2);
Console.Read();
输出结果:
//结果:
//a is 2,b is 1
//str1 is abc,str2 is ab
从运行结果可以看出:str2 的值还是 ab ,并没有随着 str1 值的改变而改变。如果string是引用类型,按理Str1和Str指针都指向同一内存地址,如果Str的内容发生改变,Str1应该也会相应变化。此例子,看着string更像是值类型。 但是MSDN却说String是引用类型。究其原因,是因为string对象是不可变的,包括长度和其中任何字符都是不可以改变的。
关于不可变数据类型,请参考:https://www.cnblogs.com/mushroom/p/4373951.html
String的不变性
string 对象称为不可变的(只读),因为一旦创建了该对象,就不能修改该对象的值。有的时候看来似乎修改了,实际是string经过了特殊处理,每次改变值时都会建立一个新的string对象,变量会指向这个新的对象,而原来的还是指向原来的对象,所以不会改变。这也是string效率低下的原因。如果经常改变string的值则应该使用StringBuilder而不使用string。
在例子中str1=”ab”,这时在内存中就将“ab”存下来,如果再创建字符串对象,其值也等于“ab”,str2=”ab”,则并非再重新分配内存空间,而是将之前保存的“ab”的地址赋给str2的引用,这就能印证例子2中的结果。而当str1=”abc”其值发生改变时,这时检查内存,发现不存在此字符串,则重新分配内存空间,存储“abc”,并将其地址赋给str1,而str2依然指向“ab”的地址。可以印证例子中的结果。
结论
String是引用类型,只是编译器对其做了特殊处理。
二、浅拷贝与深拷贝
也许会有人这样解释C# 中浅拷贝与深拷贝区别:
浅拷贝是对引用类型拷贝地址,对值类型直接进行拷贝。
不能说它完全错误,但至少还不够严谨。比如:string 类型咋说?
其实,我们可以通过实践来寻找答案。
首先,定义以下类型:
int 、string 、enum 、struct 、class 、int[ ] 、string[ ]
代码如下:
//枚举
public enum myEnum
{ _1 = , _2 = } //结构体
public struct myStruct
{
public int _int;
public myStruct(int i)
{ _int = i; }
} //类
class myClass
{
public string _string;
public myClass(string s)
{ _string = s; }
} //ICloneable:创建作为当前实例副本的新对象。
class DemoClass : ICloneable
{
public int _int = ;
public string _string = "";
public myEnum _enum = myEnum._1;
public myStruct _struct = new myStruct();
public myClass _class = new myClass("");
//数组
public int[] arrInt = new int[] { };
public string[] arrString = new string[] { "" }; //返回此实例副本的新对象
public object Clone()
{
//MemberwiseClone:返回当前对象的浅表副本(它是Object对象的基方法)
return this.MemberwiseClone();
}
}
注意:
ICloneable 接口:支持克隆,即用与现有实例相同的值创建类的新实例。
MemberwiseClone 方法:创建当前 System.Object 的浅表副本。
接下来,构建实例A ,并对实例A 克隆产生一个实例B。
然后,改变实例B 的值,并观察实例A 的值会不会被改变。
代码如下:
class 浅拷贝与深拷贝
{
static void Main(string[] args)
{
DemoClass A = new DemoClass();
//创建实例A的副本 --> 新对象实例B
DemoClass B = (DemoClass)A.Clone(); B._int = ;
Console.WriteLine(" int \t\t A:{0} B:{1}", A._int, B._int); B._string = "";
Console.WriteLine(" string \t A:{0} B:{1}", A._string, B._string); B._enum = myEnum._2;
Console.WriteLine(" enum \t\t A:{0} B:{1}", (int)A._enum, (int)B._enum); B._struct._int = ;
Console.WriteLine(" struct \t A:{0} B:{1}",
A._struct._int, B._struct._int); B._class._string = "";
Console.WriteLine(" class \t\t A:{0} B:{1}",
A._class._string, B._class._string); B.arrInt[] = ;
Console.WriteLine(" intArray \t A:{0} B:{1}",
A.arrInt[], B.arrInt[]); B.arrString[] = "";
Console.WriteLine(" stringArray \t A:{0} B:{1}",
A.arrString[], B.arrString[]); Console.ReadKey();
}
}
结果如下:
从最后的输出结果,我们得知:
对于内部的Class 对象和数组,则Copy 一份地址。[ 改变B 时,A也被改变了 ]
而对于其它内置的int / string / enum / struct / object 类型,则Copy 一份值。
有一位网友说:string 类型虽然是引用类型,但是很多情况下.Net 把string 做值类型来处理,我觉得string 应该也是按照值类型处理的。
这说明他对string 类型还不够了解。
可以肯定的是:string 一定是引用类型。那它为什么是深拷贝呢?
如果你看一下string 类型的源代码就知道了:
//表示空字符串。此字段为只读。
public static readonly string Empty;
答案就在于 string 是 readonly 的,当改变 string 类型的数据值时,将重新分配了内存地址。
浅拷贝:给对象拷贝一份新的对象。
浅拷贝的定义 —— 只对值类型(或string)类型分配新的内存地址。
深拷贝:给对象拷贝一份全新的对象。
深拷贝的定义 —— 对值类型分配新的内存地址,引用类型、以及引用类型的内部字段分配的新的地址。
我是这么定义的:浅拷贝,换汤不换药。
三、Clone()方法
例如我有一个简单的类:
class People
{
public int _age;
public string _name;
public People(int Age,string Name)
{
_age = Age;
_name = Name;
}
}
常见的赋值语句,如:
People Mike = new People(,"Mike");
People Mike2 = Mike;
这是浅复制,共享同一块内存,类似指针,即Mike2与Mike对象同时指向了Mike新建时所申请的内存。
现在我为People类增加一个Clone()方法:
class People
{
public int _age;
public string _name;
public People(int Age,string Name)
{
_age = Age;
_name = Name;
}
public object Clone()
{
People MySelf = new People(this._age,this._name);
return MySelf; }
}
很明显,调用Clone()方法返回的对象是一个全新的对象,是新实例化出来的对象但是与原对象在值上相等。
People Mike = new People(,"Mike");
People Mike2 = Mike;
People Mike3 = Mike.Clone() as People;
Mike2与Mike3在值上相等,但实际是完全独立的对象。
Mike2._name = "Jone";
//执行上述代码后,Mike的_name属性跟着改变了,而Mike3不变。
C# 之String以及浅拷贝与深拷贝的更多相关文章
- [转] js对象浅拷贝和深拷贝详解
本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: var Chinese = ...
- 渐析java的浅拷贝和深拷贝
首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝. 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所 ...
- 关于JavaScript的浅拷贝和深拷贝
在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...
- c#中浅拷贝和深拷贝的理解
c#中拷贝有浅拷贝和深拷贝之分. 例如对象A,其中有值类型字段和引用类型字段: 1.浅拷贝: 对于值类型字段,直接逐位复制到新拷贝的副本对象中,修改副本的字段的值,不会影响源对象中字段的值: 对于引用 ...
- C#浅拷贝与深拷贝区别
也许会有人这样解释C# 中浅拷贝与深拷贝区别: 浅拷贝是对引用类型拷贝地址,对值类型直接进行拷贝. 不能说它完全错误,但至少还不够严谨.比如:string 类型咋说? 其实,我们可以通过实践来寻找答案 ...
- IOS的浅拷贝和深拷贝
什么是深拷贝和浅拷贝 浅拷贝:就是指针的复制,拷贝的指针跟原指针指向内存中的同一个位置的对象.至于对象的引用计数值是否+1,就是看拷贝的指针赋给给的变量是Strong类型的,还是week类型的. 如果 ...
- 编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]
前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...
- c# 关于浅拷贝和深拷贝
class Program { static void Main(string[] args) { //浅拷贝 Person p1 = new Person(); p1.Name = "张三 ...
- C#的浅拷贝和深拷贝
C#中有两种类型变量,一种 是值类型变量,一种是引用类型变量 对于值类型变量,深拷贝和前拷贝都是通过赋值操作符号(=)实现,其效果一致,将对象中的值类型的字段拷贝到新的对象中.这个很容易理解. 本文重 ...
随机推荐
- 在centos7升级jenkins
找到jenkins的位置 使用下面的命令 ps -aux | grep jenkins enkins 5954 7.9 22.5 2695800 421088 ? Ssl 20:5 ...
- 构建NetCore应用框架之实战篇(四):BitAdminCore框架1.0登录功能细化及技术选型
本篇承接上篇内容,如果你不小心点击进来,建议从第一篇开始完整阅读,文章内容继承性连贯性. 构建NetCore应用框架之实战篇系列 一.BitAdminCore框架1.0版本 1.1.0版本是指最小版本 ...
- F#语言入门之什么是F#语言
F#是一种函数式编程语言,可以轻松编写正确且可维护的代码. F#编程主要涉及定义类型推断和自动泛化的类型和函数. 这使您可以将焦点保留在问题域上并操纵其数据,而不是编程的细节. open System ...
- MaxScript调用DotNet时命名空间的问题
Fn GetSpecialFolder argEnumName = (DotNetClass "System.Environment").GetFolderPath (Execut ...
- AtomicInteger源码解析
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.原子类 可以实现一些原子操作 基于CAS 下面就以AtomicInteger为例. 2.AtomicIn ...
- springboot入门之简单demo
项目构建 我们采用maven构建SpringBoot工程,首先创建一个maven工程,对应的pom文件如下: <properties> <java.version>1.8< ...
- iOS--线程的创建
1.获取当前线程 NSThread *current=[NSThread currentThread]; 2.获取主线程的另外一种方式 NSThread *main=[NSThread mainThr ...
- 【LeetCode】414. 第三大的数
给定一个非空数组,返回此数组中第三大的数.如果不存在,则返回数组中最大的数.要求算法时间复杂度必须是O(n). 示例 1: 输入: [3, 2, 1] 输出: 1 解释: 第三大的数是 1. 示例 2 ...
- (转)飘逸的python - 增强的格式化字符串format函数
原文:https://blog.csdn.net/handsomekang/article/details/9183303 Python字符串格式化--format()方法-----https://b ...
- 漫谈NIO(2)之Java的NIO
1.前言 上章提到过Java的NIO采取的是多路IO复用模式,其衍生出来的模型就是Reactor模型.多路IO复用有两种方式,一种是select/poll,另一种是epoll.在windows系统上使 ...