相信大家在工作中都遇到过对象拷贝的情况,众所周知程序中的拷贝分为两种深拷贝(或者称为clone),浅拷贝.net为我们提供比较方便的MemberwiseClone()这儿就不提及了!今天我们讨论一下关于深拷贝的问题!
平常我常用的方式有3种
1.手动赋值
对象有多少属性就手动赋值多少,如果有子对象以此类推!
优点: 这种方式几乎万能,什么情况下我们几乎都能赋值但对象图比较小的时候这招很不错效率也很高!
缺点:繁琐特别是当对象图大(子对象,子子对象很多之类的)这种机械式操作一般会分给新人做,当然新人有砸电脑的冲动了(哈哈哈)!
2.利用序列化反序列化
下面定义了一个扩展方法在object对象上,这样其他对象都能使用这个深拷贝方法了(方便吧)!
注意:被拷贝的对象上须添加[Serializable]特性标签
public static class CloneHelper
{
/// <summary>
/// 深克隆一个对象
/// </summary>
/// <param name="oriObj"></param>
/// <returns></returns>
public static object DeepClone(this object oriObj)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Context = new StreamingContext(StreamingContextStates.Clone);
formatter.Serialize(stream, oriObj);
stream.Position = 0;
return formatter.Deserialize(stream);
}
}
}
优点:方便,项目中几乎都能用!不怕对象图大就怕那娃不听话!
缺点:当实现一些比较特别的拷贝的时候比如不同命名空间一下类结构相同之间的赋值拷贝的情况无法直接用!
3.利用反射
public class DynamicLoadP
{
/// <summary>
/// 利用反射动态转换类型支持还不完善(如:不支持枚举类型,枚举类型将掉值)
/// </summary>
/// <typeparam name="H"></typeparam>
/// <typeparam name="T"></typeparam>
/// <param name="h"></param>
/// <returns></returns>
public static T ConvertTo<H, T>(H h)
{
//这个方法太复杂了,没办法,两种协议中涉及到的相关转换对象太多,手动赋值工作量太大。
T t = default(T);
try
{
Type tTypeRoot = typeof(T);
Type hTypeRoot = typeof(H);
if (tTypeRoot.IsArray)
{
if (!hTypeRoot.IsArray)
{
return default(T);
}
//数组处理方式不同
//得到数组
var hArrValue = h as Array;
if (hArrValue != null)
{
Type tpTitem = tTypeRoot.GetElementType();
Type hpTitem = hTypeRoot.GetElementType();
var tArrary = Activator.CreateInstance(tTypeRoot, hArrValue.Length) as Array;
var ctMethod = typeof(DynamicLoadP).GetMethod("ConvertTo");
var ctMethodT = ctMethod.MakeGenericMethod(new[] { hpTitem, tpTitem });
for (int i = 0; i < hArrValue.Length; i++)
{
var hItemValue = hArrValue.GetValue(i);
var hValueT = ctMethodT.Invoke(null, new[] { hItemValue });
if (tArrary != null)
{
tArrary.SetValue(hValueT, i);
}
}
object tObj = tArrary;
t = (T)tObj;
}
else
{
return default(T);
}
}
else
{
t = (T)Activator.CreateInstance(typeof(T));
}
var tInstanceType = t.GetType();
var tItemPropertieS = tInstanceType.GetProperties();
foreach (var tItemPropertie in tItemPropertieS)
{
var hInstanceType = h.GetType();
var hItemPropertieS = hInstanceType.GetProperties();
foreach (var hItemPropertie in hItemPropertieS)
{
if (tItemPropertie.Name.Equals(hItemPropertie.Name, StringComparison.CurrentCultureIgnoreCase))
{
if (!tItemPropertie.PropertyType.Namespace.Contains("System"))
{
Type tpT = tItemPropertie.PropertyType;
Type hpT = hItemPropertie.PropertyType;
var hpValue = hItemPropertie.GetValue(h, null);
var ctMethod = typeof(DynamicLoadP).GetMethod("ConvertTo");
var ctMethodT = ctMethod.MakeGenericMethod(new[] { hpT, tpT });
var hValueT = ctMethodT.Invoke(null, new[] { hpValue });
tItemPropertie.SetValue(t, hValueT, null);
}
else
{
var hValue = hItemPropertie.GetValue(h, null);
if (tItemPropertie.PropertyType.FullName != hItemPropertie.PropertyType.FullName)
{
try
{
if (hValue.ToString().Equals("n/a", StringComparison.InvariantCultureIgnoreCase))
{
hValue = "0";
}
Type iConvertibleType = typeof(IConvertible);
if (hItemPropertie.PropertyType.IsSubclassOf(iConvertibleType))
{
tItemPropertie.SetValue(t,
Convert.ChangeType(hValue,
tItemPropertie.PropertyType), null);
}
if (tItemPropertie.PropertyType == typeof(string))
{
tItemPropertie.SetValue(t,
hValue.ToString(),
null);
}
else
{
throw new TypeInitializationException(tItemPropertie.PropertyType.FullName+"-->"+ hItemPropertie.PropertyType.FullName
, new Exception("转换过程中类型发生异常")); ;
}
}
catch (Exception ex)
{
// logger.Error("【" + DateTime.Now.ToString() + "】" + "ConvertTo<H,T>(H h):{0}", tItemPropertie.Name + "动态赋值失败" + ex.ToString());
}
}
else
{
//转换时候如此多的属性,难免会报错,不兼容的值,只有下策吞异常处理逻辑了
try
{
tItemPropertie.SetValue(t, hValue, null);
}
catch (Exception ex)
{
//已知错误不处理,这儿最好不用Exception,为了掩饰方便就不去追究了哈
}
}
}
}
}
}
return t;
}
catch (Exception ex)
{
// logger.Error("【" + DateTime.Now.ToString() + "】" + "ConvertTo<H,T>(H h):{0}", "动态赋值失败" + ex.ToString());
return t;
}
}
}
优点:方法3支持不同命名空间下的copy(这儿有转换意思),命名匹配规则可以加入自己的逻辑,方法扩展性强!
缺点:基于反射效率相对较低不过一般情况下这点效率不是什么问题瓶颈一般不在这儿!