最近在处理一些数据交互的问题,碰到了一个稀疏平常的问题——克隆。将一份源数据克隆出来,单独处理,不影响源数据,比如小怪的属性,buff等等,但平时要不数据在gameobject上,实例化时就复制了一份,要不就直接是类池直接反射实例化出来,没有直接使用源数据的机会。
但最近碰到了一个问题,就是怪物的属性不在gameoject上,而是单独的配置表,实时导入的。或者是lua转的委托保存在数据中,这时既没有类池,也没有gameobject,就需要手动进行克隆。
最直接的克隆就是将源数据传入构造函数,全部克隆复制一遍。
public class EntityAction
{
public ScanEntity scanEntity;
public int id;
public int type;
public bool unCut;
public float readyTime;
public float maxReadyTime;
public float coolTime;
public float maxCoolTime;
public Action<EntityAction> startWork;
public Action<EntityAction> updataWork;
public Action<EntityAction> cutWork;
public Action<EntityAction> finishWork;
public Action<EntityAction> triger;
public Action<EntityAction> startCool;
public Action<EntityAction> finishCool;
public List<EntityAction> actionList;
public EntityAction() { }
public EntityAction(EntityAction action)
{
id = action.id;
type = action.type;
unCut = action.unCut;
readyTime = action.readyTime;
maxReadyTime = action.maxReadyTime;
coolTime = action.coolTime;
maxCoolTime = action.maxCoolTime;
startWork = action.startWork.Clone() as Action<EntityAction>;
updataWork = action.updataWork.Clone()as Action<EntityAction>;
cutWork = action.cutWork.Clone() as Action<EntityAction>;
finishWork = action.finishWork.Clone() as Action<EntityAction>;
triger = action.triger.Clone() as Action<EntityAction>;
startCool = action.startCool.Clone() as Action<EntityAction>;
finishCool = action.finishCool.Clone()as Action<EntityAction>;
actionList = new List<EntityAction>();
foreach (var action in entity.actionList)
{
actionList.Add(new EntityAction(action));
}
}
}
这样写简单粗暴,而且还能根据需求自由更改克隆内容,但也非常麻烦,大部分都是重复书写,而且在为数据结构添加字段时要记得在克隆时也复制一份,而且也要注意该字段是不是也有自己的克隆需求。至此我就想能不能简化该步骤,只是数值的转移,应该可以智能一点。
然后,我注意到了这个,Clone(),在为委托转移时碰到的
startWork = action.startWork.Clone() as Action<EntityAction>;
右键进去可以发现,Delegate继承一个接口叫ICloneable,要实现一个方法
object Clone();
返回一个object,应该就是自定义克隆效果,如果我们让自定义数据继承然后实现,在数值复制时,判断是不是ICloneable,是就调用Clone,不是就直接复制。
自定义克隆有了,就剩下字段自动化复制的。作为数据类,其中不会有太多无用数据,就默认所有数据全部复制,既然不手动复制,就要反射获取,然后一一遍历复制。
public class Father: ICloneable
{
public string name;
public int id;
public Son son;
public List<Son> sons;
public Son[,] sons2;
public Dictionary<int, Son> sons3;
public Son[][] sons4;
public object Clone()
{
//反射获取类型
Type t = GetType();
//获取所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
//实例化一个新对象
object resurt = Activator.CreateInstance(t);
//遍历所有对象一一赋值
foreach (FieldInfo field in fieldInfos)
{
field.SetValue(resurt, GetValue(field.GetValue(this)));
}
return resurt;
//根据类型不同返回不同的克隆对象
object GetValue(object objRe)
{
if (objRe == null) return null;
if (objRe is ICloneable clone)
{
object cloneObj = clone.Clone();
return cloneObj;
}
else
{
return objRe;
}
}
}
}
public class Son:ICloneable
{
public string name;
public int id;
public Son() { }
public Son(int id)
{
this.id = id;
name = "old";
}
public object Clone()
{
//反射获取类型
Type t = GetType();
//获取所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
//实例化一个新对象
object resurt = Activator.CreateInstance(t);
//遍历所有对象一一赋值
foreach (FieldInfo field in fieldInfos)
{
field.SetValue(resurt, GetValue(field.GetValue(this)));
}
return resurt;
//根据类型不同返回不同的克隆对象
object GetValue(object objRe)
{
if (objRe == null) return null;
if (objRe is ICloneable clone)
{
object cloneObj = clone.Clone();
return cloneObj;
}
else
{
return objRe;
}
}
}
}
写好,开测!但还在写测试代码时,就发现,等等,list,dic之类的有没有clone,一一点进去看继承,如下
array:有
IList:没有
IDic:没有
没有肯定不行,但有的测一下,能不能递归调用。可惜不行,浅表复制,不会递归,所有都要手动处理一下。
Ilist和IDic都好处理,遍历元数据,再一一添加进新数据,数组就麻烦一点,要先知道是几维数组,再确定维度长度,然后创建数组,递归赋值。
这样太长了,每一个类都复制一遍不太合适,所以放在静态工具类中,数据调用一下就行
public static object Clone<T>(T obj)
{
//反射获取类型
Type t = obj.GetType();
//获取所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
//实例化一个新对象
object resurt = Activator.CreateInstance(t);
//遍历所有对象一一赋值
foreach (FieldInfo field in fieldInfos)
{
field.SetValue(resurt, GetValue(field.GetValue(obj)));
}
return resurt;
//根据类型不同返回不同的克隆对象
object GetValue(object objRe)
{
if (objRe == null) return null;
//自定义克隆对象
if (objRe is ICloneable clone)
{
object cloneObj;
//判断对象是不是数组
if (clone is Array array)
{
//获取维度
int rank = array.Rank;
//获取维度长度
int[] lenghs = new int[rank];
for (int i = 0; i < rank; i++)
{
lenghs[i] = array.GetLength(i);
}
//创建数组
Array cloneArray = Array.CreateInstance(array.GetType().GetElementType(),lenghs);
//声明一个维度指针
int[] ints = new int[rank];
//开始遍历赋值
ForEach(rank - 1, ints);
cloneObj = cloneArray;
//i是维度,每一次递归进入就是深入一层维度,直到到最后一层开始遍历全部长度赋值,然后退出本层
//上一层维度指针数加一,再进来最后一层遍历全部长度赋值,直到所有位置赋值
void ForEach(int i, int[] ints)
{
//遍历该维度全部长度
for (int j = 0; j < lenghs[i]; j++)
{
//调整维度指针的该维度
ints[i] = j;
//判断是不是最后一层维度,不是就深入
if (i > 0)
{
ForEach(i - 1, ints);
}
else
{
//最后一层维度,调用Array.SetValue赋值
object arrayObj = array.GetValue(ints);
cloneArray.SetValue(GetValue(arrayObj), ints);
}
}
}
}
else
{
cloneObj = clone.Clone();
}
return cloneObj;
}
else if (objRe is IList ilist)
{
IList list = (IList)Activator.CreateInstance(objRe.GetType());
foreach (var value in ilist)
{
list.Add(GetValue(value));
}
return list;
}
else if (objRe is IDictionary iDic)
{
IDictionary list = (IDictionary)Activator.CreateInstance(objRe.GetType());
foreach (DictionaryEntry value in iDic)
{
list.Add(GetValue(value.Key), GetValue(value.Value));
}
return list;
}
else
{
return objRe;
}
}
}
public class Father: ICloneable
{
public string name;
public int id;
public Son son;
public List<Son> sons;
public Son[,] sons2;
public Dictionary<int, Son> sons3;
public Son[][] sons4;
public object Clone()
{
return Utilities.Clone(this);
}
}
public class Son:ICloneable
{
public string name;
public int id;
public Son() { }
public Son(int id)
{
this.id = id;
name = "old";
}
public object Clone()
{
return Utilities.Clone(this);
}
}
开始测试,测试代码如下
Father father = new Father();
father.name = "father";
father.son = new Son(1);
father.sons = new List<Son>() { new Son(2), new Son(3) };
father.sons2 = new Son[,] { { new Son(4), new Son(5) }, { new Son(6), new Son(7) } };
father.sons3 = new Dictionary<int, Son> { { 8, new Son(8) }, { 9, new Son(9) } };
father.sons4 = new Son[][] { new Son[] {new Son(10) },new Son[] {new Son(11) } };
Father father1 = father.Clone() as Father;
father.name = "new father";
father.son.name = "new";
father.sons[0].name = "new";
father.sons.Add(null);
father.sons2[1,1].name = "new";
father.sons3[8].name = "new";
father.sons4[0][0].name = "new";
Debug.Log(father1.name);
Debug.Log("---------Son-----------");
Debug.Log(father1.son.name);
Debug.Log("---------List-----------");
foreach (var son in father1.sons)
{
Debug.Log(son.id + " " + son.name);
}
Debug.Log("---------Array-----------");
foreach (var son in father1.sons2)
{
Debug.Log(son.id + " " + son.name);
}
foreach (var son in father1.sons4)
{
Debug.Log(son[0].id + " " + son[0].name);
}
Debug.Log("---------Dic-----------");
foreach (var son in father1.sons3.Values)
{
Debug.Log(son.id + " " + son.name);
}
以上就是临时突发感想写出来的,如果有什么运行问题,或者更好的方法,请联系:
QQ:1768927122