C# 拷贝数组的几种方式
C#中数组复制有多种方法
数组间的复制,int[] pins = {9,3,4,9};int [] alias = pins;这里出了错误,也是错误的根源,以上代码并没有出错,但是根本不是复制,因为pins和alias都是引用,存在于堆栈中,而数据9,3,4,3是一个int对象存在于堆中,int [] alias = pins;只不过是创建另一个引用,alias和pins同时指向{9,3,4,3},当修改其中一个引用的时候,势必影响另一个。复制的意思是新建一个和被复制对象一样的对象,在C#语言中应该有如下4种方法来复制。
方法一:使用for循环

int []pins = {9,3,7,2}
int []copy = new int[pins.length];
for(int i =0;i!=copy.length;i++)
{
copy[i] = pins[i];
}


方法二:使用数组对象中的CopyTo()方法

int []pins = {9,3,7,2}
int []copy2 = new int[pins.length];
pins.CopyTo(copy2,0);

方法三:使用Array类的一个静态方法Copy()

int []pins = {9,3,7,2}
int []copy3 = new int[pins.length];
Array.Copy(pins,copy3,copy.Length);

方法四:使用Array类中的一个实例方法Clone(),可以一次调用,最方便,但是Clone()方法返回的是一个对象,所以要强制转换成恰当的类类型。


int []pins = {9,3,7,2}
int []copy4 = (int [])pins.Clone();

方法五:

string[] student1 = { "$", "$", "c", "m", "d", "1", "2", "3", "1", "2", "3" };
string[] student2 = { "0", "1", "2", "3", "4", "5", "6", "6", "1", "8", "16","10","45", "37", "82" };
ArrayList student = new ArrayList();   
foreach (string s1 in student1)
{
      student.Add(s1);               
}
foreach (string s2 in student2)
{
      student.Add(s2);
}
string[] copyAfter = (string[])student.ToArray(typeof(string));

两个数组合并,最后把合并后的结果赋给copyAfter数组,这个例子可以灵活变通,很多地方可以用。
首先说明一下,数组是引用类型的,所以注意不要在复制时复制了地址而没有复制数值哦!
其实在复制数组的时候,一定要用new在堆中开辟一块新的空间专门用于存放数组,这样才是有效的。
1
int[] pins = { 9, 3, 7, 2 };
int[] copy=new int[pins.length];
for (int i = 0; i < copy.length; i++)
{
copy[i] = pins[i];
}
(2)
int[] copy = new int[pins.Length];
pins.CopyTo(copy, 0);
(3)
Int[] pins= new int[4]{9,3,7,2};
Int[] alias=pins;
注意这种复制只是一种引用而已,只是把数据的地址传递给了alias数组,所以不太推荐这种方式来复制数组;
(4)
Array.Copy(pins,copy,copy.Length)
(5)
Int[] copy=(int[])pins.Clone();
这里说明一下为什么要用到int[]的强制类型转换,原因就在于Clone的结果类型是object的,所以需要强制转换为int[]
Object类其实就是我们所有类的基类。
C#数组中CopyTo()和Clone()的区别(转)
CopyTo()和Clone()
1.CopyTo()和Clone()
相信大多数C#程序员都有查阅MSDN的好习惯,但是MSDN中提到这两个方法最大的区别就是:一个方法创建了一个新Array对象,一个方法只是复制了Array引用.这句话本身没有错误,而且也正是他们的区别所在.只是这样会让人感到很迷惑.到底是什么区别呢?这里还是先说说他们的共同点:CopyTo()和Clone()都属于浅拷贝,这一点是毋庸置疑的.对于浅拷贝:如果数组中的成员为值类型(如:int,float,double,byte等),则完全复制数值到目标数组中,如果是引用类型(如用户自定义类型:class Student,class People,或者是类库中的类类型:ArrayList等),则指复制引用给目标数组.
那么CopyTo()和Clone()方法的区别是什么呢?其实他们的区别,也就是MSDN上说的最大的区别就是用法上的区别.我们可以在VS弹出智能提示的时候看看他们的返回值,CopyTo()的返回值是void,使用方法如下Array1.CopyTo(Array2,0);其中Array2必须是一个已经实例化的数组.而Clone()的返回值是object.使用方法如下Array2 = Array1.Clone();其中Array2不必实例化.这样,我相信理解这两个方法的区别就很容易了.本质上并没有什么区别.都属于浅拷贝.如果拷贝所有的数组,就是用Clone().但是如果只是拷贝一部分,就可以选择CopyTo()了,CopyTo()的参数提示是这样的CopyTo(Array array,int Index).第二个参数index(索引)是指
明从数组中的第几个对象开始复制.
2.浅拷贝和深拷贝的区别.
如上面所说的,浅拷贝对于值类型则复制值,对于引用类型则复制对象的引用(类似于指针).深拷贝则是完完全全的创建一个个新对象.对原来数组中的所有对象全部创建新对象.对新数组中的修改不会影响原来数组中的值或对象.但是如何实现深拷贝呢?.NET库中似乎没有深拷贝的方法.这和实现深拷贝的原理有关系.若用户希望实现深拷贝.希望出现两个完全一样但又互不影响的数组.则必须自己写方法,对原数组中的每个对象实现拷贝,层层深入,直到这个对象中的对象中的对象……中的对象为值类型为止,因为只有值类型才是完全拷贝,对一个值进行修改不会影响另一个相同的值.这么说又有点难理解了. 1. 深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等。
考虑以下写法
int source = int.MaxValue;//(1)初始化源对象为整数的最大值2,147,483,647
int dest = source;//(2)赋值,内部执行深拷贝
dest = 1024;//(3)对拷贝对象进行赋值
source = 2048;//(4)对源对象进行赋值
首先(2)中将source赋给dest,执行了深拷贝动作,其时dest和source的值是一样的,都是int.MaxValue;(3)对dest进行修改,dest值变为1024,由于是深拷贝,因此不会运行source,source仍然是int.MaxValue;(4)对source进行了修改,同样道理,dest仍然是1024,同时int.MaxValue的值也不变,仍然是2,147,483,647;只有source变成了2048。
再考虑以下写法
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}

Point source = new Point(10, 20);
Point dest = source;

dest.X = 20
当dest.X属性变成20后,source的X属性仍然是10
2. 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。举个例子,一个人一开始叫张三,后来改名叫李四了,可是还是同一个人,不管是张三缺胳膊少腿还是李四缺胳膊少腿,都是这个人倒霉。比较典型的就有Reference(引用)对象,如Class(类)。
考虑以下写法
class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}

Point source = new Point(10, 20);
Point dest = source;
dest.X = 20;
由于Point现在是引用对象,因此Point dest=source的赋值动作实际上执行的是浅拷贝,最后的结果应该是source的X字段值也变成了20。即它们引用了同一个对象,仅仅是变量明s
ource和dest不同而已。
3. 引用对象的浅拷贝原理
引用对象之间的赋值之所以执行的是浅拷贝动作,与引用对象的特性有关,一个引用对象一般来说由两个部分组成
(1)一个具名的Handle,也就是我们所说的声明(如变量)
(2)一个内部(不具名)的对象,也就是具名Handle的内部对象。它在Manged Heap(托管堆)中分配,一般由新增引用对象的New方法是进行创建
如果这个内部对象已被创建,那么具名的Handle就指向这个内部对象在Manged Heap中的地址,否则就是null(从某个方面来讲,如果这个具名的handle可以被赋值为null,说明这是一个引用对象,当然不是绝对)。两个引用对象如果进行赋值,它们仅仅是复制这个内部对象的地址,内部对象仍然是同一个,因此,源对象或拷贝对象的修改都会影响对方。这也就是浅拷贝
4. 引用对象如何进行深拷贝
由于引用对象的赋值仅仅是复制具名Handle(变量)指向的地址,因此要对引用对象进行深拷贝就要重新创建一份该对象的实例,并对该对象的字段进行逐一赋值,如以下写法
class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}

Point source = new Point(10, 20);
Point dest = new Point(source.X, source.Y);
//或以下写法
//Point dest = new Point()
/
/dest.X = source.X
//dest.Y = source.Y
其时,source和dest就是两个互相独立的对象了,两者的修改都不会影响对方

5.一些需要注意的东西
(1):String字符串对象是引用对象,但是很特殊,它表现的如值对象一样,即对它进行赋值,分割,合并,并不是对原有的字符串进行操作,而是返回一个新的字符串对象
(2):Array数组对象是引用对象,在进行赋值的时候,实际上返回的是源对象的另一份引用而已;因此如果要对数组对象进行真正的复制(深拷贝),那么需要新建一份数组对象,然后将源数组的值逐一拷贝到目的对象中
在C#中怎么部分复制数组?
Array.Copy()方法有四种重载,其中有一个重载可以指定从数组的第几个元素开始复制,复制多少个。
具体用法如下:
public static void Copy(
    Array sourceArray,      //包含要复制的数据
typeof array    long sourceIndex,        //64 位整数,它表示 sourceArray 中复制开始处的索引
    Array destinationArray,  //目的数组
    long destinationIndex,  //64 位整数,它表示 destinationArray 中存储开始处的索引
    long length              //64 位整数,它表示要复制的元素数目
)
C# 数组复制的另外两种方式。字节偏移复制与安全复制。
代码如下:
static void Main(string[] args)
{
int[] src = new[] { 1, 2, 3, 4, 5, 6 };
const int destLen = 4;//目标数组大小
int int_size = sizeof(int);//用于获取值类型的字节大小。
int[] dest = new int[destLen];
//只支持基元类型,按字节偏移复制
Buffer.BlockCopy(src, (src.Length - destLen) * int_size, dest, 0, destLen * int_size);
foreach (var i in dest)
{
Console.Write(i + " ");
}
Console.WriteLine("\n-------------------------------------------");
string[] srcstr = new[] { "A", "B", "C", "D", "E", "F" };
object[] destobj = new object[src.Length - 2];
//移除的元素个数
const int dellen = 2;
//保证不破坏目标数组的元素(回滚)。不装箱、拆箱、或向下转换,否则报错。
//如果srcstr改为src则报错,因为装箱。
Array.ConstrainedCopy(srcstr, dellen, destobj, 0, srcstr.Length - dellen);
foreach (var s in destobj)
{
Console.Write(s + " ");
}
}
效果如下:
C#中的浅拷贝与深拷贝
浅拷贝:
如果数组中的成员为值类型(如:int,float,double,byte等),则完全复制数值到目标数组中,如果是引用类型(如用户自定义类型:class Student,class People,或者是类库中的类类型:ArrayList等),则指复制引用给目标数组。
文字有时候不如代码来得容易理解.但是这里也许用图更容易理解,看下图:
假定创建一个学生类数组Student[],然后浅拷贝到另一个学生类数组Student1[]中
从图中很容易看出所谓的浅拷贝对于引用类型,仅仅只是复制引用.通过一个数组修改内存中的值会影响另一个数组对内存对象的引用。
Array 类中的CopyTo和Clone函数都属于浅拷贝。
深拷贝则是完完全全的创建一个个新对象.对原来数组中的所有对象全部创建新对象.对新数组中的修改不会影响原来数组中的值或对象。
C#线程锁使用全功略
前两篇简单介绍了线程同步lock,Monitor,同步事件EventWaitHandler,互斥体Mutex的基本用法,在此基础上,我们对 它们用法进行比较,并给出什么时候需要锁什么时候不需要的几点建议。最后,介绍几个FCL中线程安全的类,集合类的锁定方式等,做为对线程同步系列的完善 和补充。
1.几种同步方法的区别
lock和Monitor是.NET用一个特殊结构实现的,Monitor对象是完全托管的、完全可移植的,并且在操作系统资源要求方 面可能更为有效,同步速度较快,但不能跨进程同步。lock(Monitor.Enter和Monitor.Exit方法的封装),主要作用是锁定临界区,使临 界区代码只能被获得锁的线程执行。Monitor.Wait和Monitor.Pulse用于线程同步,类似信号操作,个人感觉使用比较复杂,容易造成死 锁。
互斥体Mutex和事件对象EventWaitHandler属于内核对象,利用内核对象进行线程同步,线程必须要在用户模式和内核模 式间切换,所以一般效率很低,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步
互斥体Mutex类似于一个接力棒,拿到接力棒的线程才可以开始跑,当然接力棒一次只属于一个线程(Thread Affinity),如果这个线程不释放接力棒(Mutex.ReleaseMutex),那么没办法,其他所有需要接力棒运行的线程都知道能等着看热 闹。
EventWaitHandle 类允许线程通过发信号互相通信。 通常,一个或多个线程在 EventWaitHandle 上阻止,直到一个未阻止的线程调用 Set 方法,以释放一个或多个被阻止的线程。
2.什么时候需要锁定
首先要理解锁定是解决竞争条件的,也就是多个线程同时访问某个资源,造成意想不到的结果。比如,最简单的情况是,一个计数器,两个线程 同时加一,后果就是损失了一个计数,但相当频繁的锁定又可能带来性能上的消耗,还有最可怕的情况死锁。那么什么情况下我们需要使用锁,什么情况下不需要 呢?
1)只有共享资源才需要锁定
只有可以被多线程访问的共享资源才需要考虑锁定,比如静态变量,再比如某些缓存中的值,而属于线程内部的变量不需要锁定。
2)多使用lock,少用Mutex
如果你一定要使用锁定,请尽量不要使用内核模块的锁定机制,比如.NET的Mutex,Semaphore,AutoResetEvent和 ManuResetEvent,使用这样的机制涉及到了系统在用户模式和内核模式间的切换,性能差很多,但是他们的优点是可以跨进程同步线程,所以应该清 楚的了解到他们的不同和适用范围。
3)了解你的程序是怎么运行的
实际上在web开发中大多数逻辑都是在单个线程中展开的,一个请求都会在一个单独的线程中处理,其中的大部分变量都是属于这个线程的,根本没有必要考虑锁 定,当然对于ASP.NET中的Application对象中的数据,我们就要考虑加锁了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。