当前位置:网站首页>C shallow copy and deep copy
C shallow copy and deep copy
2022-07-02 02:59:00 【FanJiangDaoHaizZ】
Preface
In actual development , We often encounter the need to copy instances ,C# There are two ways to copy , One is light copy , One is deep copy . Shallow copy only copies value types, not reference types . Deep copy copies value types and reference types , You can choose which copy to use according to the actual situation
Shallow copy
Shallow copy requires that the class to be copied inherit ICloneable Interface
First create a class that inherits from the shallow copy
class ShallowCopyDemoClass : ICloneable
{
public int intValue = 1;
public string strValue = "1";
public PersonEnum pEnum = PersonEnum.EnumA;
public PersonStruct pStruct = new PersonStruct() {
StructValue = 1 };
public Person pClass = new Person("1");
public int[] pIntArray = new int[] {
1 };
public string[] pStringArray = new string[] {
"1" };
#region ICloneable member
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
class Person
{
public string Name;
public Person(string name)
{
Name = name;
}
}
public enum PersonEnum
{
EnumA = 0,
EnumB = 1
}
public struct PersonStruct
{
public int StructValue;
}
public class FieldsClass
{
public string fieldA;
public string fieldB;
public FieldsClass()
{
fieldA = "A public field";
fieldB = "Another public field";
}
}
Test code
ShallowCopyDemoClass shallowCopyDemoClass = new ShallowCopyDemoClass();
ShallowCopyDemoClass sCopy = shallowCopyDemoClass.Clone() as ShallowCopyDemoClass;
sCopy.pClass.Name = "2";
sCopy.pEnum = PersonEnum.EnumB;
sCopy.pStruct.StructValue = 2;
sCopy.pIntArray[0] = 2;
sCopy.pStringArray[0] = "2";
// Shallow copy common value class reference
Console.WriteLine("Class1: {0}", shallowCopyDemoClass.pClass.Name);
Console.WriteLine("Class2: {0}", sCopy.pClass.Name);
Console.WriteLine("Enum1: {0}", shallowCopyDemoClass.pEnum);
Console.WriteLine("Enum2: {0}", sCopy.pEnum);
Console.WriteLine("Struct1: {0}", shallowCopyDemoClass.pStruct.StructValue);
Console.WriteLine("Struct2: {0}", sCopy.pStruct.StructValue);
Console.WriteLine("Array1: {0}", shallowCopyDemoClass.pIntArray[0]);
Console.WriteLine("Array2: {0}", sCopy.pIntArray[0]);
Output is as follows :
Through the output, you can find , The shallow copy of the real column shares the reference type with the original real column
Deep copy
First, create the class that needs to be copied , Convenient test (TestB and DeepCopyDemoClass Cross reference )
[Serializable]
public class DeepCopyDemoClass
{
public string Name {
get; set; }
public int ID;
public int[] pIntArray {
get; set; }
public Address Address {
get; set; }
public DemoEnum DemoEnum {
get; set; }
public Dictionary<int, int> dict;
// DeepCopyDemoClass Is cited in TestB object ,TestB Class references again DeepCopyDemoClass object , This leads to cross referencing
public TestB TestB {
get; set; }
public override string ToString()
{
return "DeepCopyDemoClassOpOp";
}
}
[Serializable]
public class TestB
{
public string Property1 {
get; set; }
public DeepCopyDemoClass DeepCopyClass {
get; set; }
public override string ToString()
{
return "TestB Class";
}
}
[Serializable]
public struct Address
{
public string City {
get; set; }
}
public enum DemoEnum
{
EnumA = 0,
EnumB = 1
}
There are many implementation schemes for deep copy , Here are three
Use reflection to realize deep copy
C# The principle of reflection can be referred to the article :C# Reflection practice When using reflection, we need to pay attention to dealing with cross references between objects . The implementation code is as follows :
public class DeepCopyHelper<T> where T : class, new()
{
/// <summary>
/// Deep copy through reflection
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopyByReflect(T obj)
{
// Store the copied classes
Dictionary<object, object> dict = new Dictionary<object, object>();
T_Copy Copy<T_Copy>(T_Copy obj)
{
Type type = obj.GetType();
//string Type and value types are returned directly
//string When the type is assigned, it will automatically new A new object is returned directly
if (obj is string || obj.GetType().IsValueType) return obj;
// If it's an array
if (obj.GetType().IsArray)
{
// Get array element type
Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
// Create an array instance
Array copied = Array.CreateInstance(elementType, array.Length);
// Copy
for (int i = 0; i < array.Length; i++)
{
try
{
object value = array.GetValue(i);
if (value != null)
copied.SetValue(Copy(value), i);
}
catch {
}
}
return (T_Copy)Convert.ChangeType(copied, obj.GetType());
}
// Reference type
// The key stored in the dictionary directly returns to the current Type All public fields of .
if (dict.ContainsKey(obj))
{
return (T_Copy)dict[obj];
}
// Create type instances
object retval = Activator.CreateInstance(obj.GetType());
dict.Add(obj, retval);
/* GetFields() Default return without parameters * Appoint BindingFlags.Instance To include example methods . Appoint BindingFlags.Static To include static methods . Appoint BindingFlags.Public Include public fields in the search . Appoint BindingFlags.NonPublic To include in the search ) Non public field of ( I.e. private 、 Internal and protected fields . Only the protected and internal fields of the base class are returned ; Do not return private fields on the base class . */
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
// Copy
foreach (FieldInfo field in fields)
{
try
{
object value = field.GetValue(obj);
// An empty value indicates a type that has not been assigned a value , Don't copy
if (value != null)
field.SetValue(retval, Copy(value));
}
catch {
};
}
return (T_Copy)retval;
}
return Copy(obj);
}
}
The test code is as follows :
DeepCopyDemoClass deepCopyDemoClass = new DeepCopyDemoClass() {
Name = "sss" };
deepCopyDemoClass.pIntArray = new int[10];
deepCopyDemoClass.dict = new Dictionary<int, int>();
deepCopyDemoClass.dict.Add(1, 1);
TestB testB = new TestB();
deepCopyDemoClass.TestB = testB;
deepCopyDemoClass.TestB.Property1 = "TestB1";
testB.DeepCopyClass = deepCopyDemoClass;
DeepCopyDemoClass dcc = DeepCopyHelper<DeepCopyDemoClass>.DeepCopyByReflect(deepCopyDemoClass);
Console.WriteLine(dcc.Name);
Console.WriteLine(" After copying :{0}", dcc.TestB.Property1);
dcc.TestB.Property1 = "TestB2";
Console.WriteLine(" Assignment after copy :{0}", dcc.TestB.Property1);
Console.WriteLine(" The value of the copied object :{0}", deepCopyDemoClass.TestB.Property1);
Console.WriteLine(" Array :{0}", dcc.pIntArray[0]);
Console.WriteLine(" Dictionaries :{0}", dcc.dict[1]);
The output is as follows
Deep copy using expression tree
public class DeepCopyHelper<T> where T : class, new()
{
// Static read-only attributes can be assigned in static constructors
public static readonly Func<T, T> s_CopyFunc;
/*1、 Static constructors are not decorated with modifiers (public,private), Because the static constructor is not called by our programmers , By .net The framework is invoked at the right time . 2、 Static constructor has no parameters , Because it is impossible for the framework to know what parameters we need to add to the function , Therefore, it is stipulated that parameters cannot be used . 3、 The static constructor must be preceded by static keyword . If you don't add this keyword , That's the ordinary constructor . 4、 Instance variables cannot be instantiated in static constructors .( Variables can be divided into class level and instance level variables , Among them, the class level ones are static Keyword modification ). 5、 When to call a static function , It is called when a class is instantiated or a static member is called , And is made up of .net Framework to call the static constructor to initialize the static member variables . 6、 There can only be one static constructor in a class . 7、 Parameterless static constructors and parameterless constructors can coexist . Because they belong to class level , One belongs to instance level , Not conflict . 8、 The static constructor will only be executed once . And it is in the characteristics 5 Call at the call time in . 9、 If you don't write a constructor in your class , Then the framework will generate a constructor for us , So if we define static variables in a class , But there is no static constructor defined , Then the framework will also help us generate a static constructor to let the framework itself call . 10、 For static fields 、 Initialization of read-only fields, etc . */
static DeepCopyHelper()
{
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "m"); // Parameters m :m =>
List<MemberBinding> memberBindingList = new List<MemberBinding>();
// Property copy
foreach (var item in typeof(T).GetProperties(bindingFlags))
{
if (!item.CanWrite) // Read only properties are not copied
{
continue;
}
MemberExpression property = Expression.Property(parameterExpression, item); // m.Name
MemberBinding memberBinding = Expression.Bind(item, property); // Name = m.Name
memberBindingList.Add(memberBinding);
}
// Field copy
foreach (var item in typeof(T).GetFields(bindingFlags))
{
MemberExpression property = Expression.Field(parameterExpression, item);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(T)), memberBindingList.ToArray());// new T() {Name = m.Name}
Expression<Func<T, T>> lambda = Expression.Lambda<Func<T, T>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
}); // m => new T() {Name = m.Name}
//Console.WriteLine(lambda.ToString());
s_CopyFunc = lambda.Compile();
}
public static T DeepCopyByExpression(T obj)
{
return s_CopyFunc(obj);
}
}
This method is actually to instance the attributes and segments of the base class in the static constructor , Use Lambda Expression is expression , Reuse lambda.Compile() Compile it into an input instance , Return a function that is a copy of the instance , And store the function in s_CopyFunc In delegate variables .
lambda The expression is printed as follows :
You can see that it is all assignment operations of class initialization .
Use binary serialization and deserialization to realize deep copy
C# Serialization and deserialization details
public class DeepCopyHelper<T> where T : class, new()
{
/// <summary>
/// Using binary serialization and deserialization
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopyWithBinarySerialize(T obj)
{
object retval;
//using Statement is released at the end of execution ms
// Equivalent to execution ms.Dispose()
// Of course, objects must inherit IDisposable Interface
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
retval = bf.Deserialize(ms);
ms.Close();
}
return (T)retval;
}
}
Test the efficiency of the three methods
The test code is as follows
DeepCopyDemoClass deepCopyDemoClassT = new DeepCopyDemoClass()
{
Name = " Zhang San ",
ID = 556684,
DemoEnum = DemoEnum.EnumA,
pIntArray = new int[5] {
1, 2, 3, 4, 5 },
Address = new Address() {
City = " China " }
};
TestB testBT = new TestB();
testBT.Property1 = "555";
deepCopyDemoClassT.TestB = testBT;
testBT.DeepCopyClass = deepCopyDemoClassT;
deepCopyDemoClassT.dict = new Dictionary<int, int>();
deepCopyDemoClassT.dict.Add(1, 2);
int testTime = 1;
Stopwatch w = new Stopwatch();
w.Start();
for (int i = 0; i < testTime; i++)
{
DeepCopyHelper<DeepCopyDemoClass>.DeepCopyByExpression(deepCopyDemoClassT);
}
w.Stop();
Console.WriteLine($" Directory tree {
testTime} Time :{
w.ElapsedMilliseconds}mm");
w.Reset();
w.Start();
for (int i = 0; i < testTime; i++)
{
DeepCopyHelper<DeepCopyDemoClass>.DeepCopyByReflect(deepCopyDemoClassT);
}
w.Stop();
Console.WriteLine($" Reflection {
testTime} Time :{
w.ElapsedMilliseconds}mm");
w.Reset();
w.Start();
for (int i = 0; i < testTime; i++)
{
DeepCopyHelper<DeepCopyDemoClass>.DeepCopyWithBinarySerialize(deepCopyDemoClassT);
}
w.Stop();
Console.WriteLine($" Binary serialization {
testTime} Time :{
w.ElapsedMilliseconds}mm");
1 Time :
100000 Time
Conclusion
If the number of copies is small , Then choose the fastest reflection , If you copy the same instance many times , Then select the method of expression tree
边栏推荐
- Principle of computer composition - interview questions for postgraduate entrance examination (review outline, key points and reference)
- AcWing 245. Can you answer these questions (line segment tree)
- CoordinatorLayout + TabLayout + ViewPager2(里面再嵌套一个RecyclerView),RecyclerView的滑动冲突解决
- How to turn off the LED light of Rog motherboard
- 2022-2028 global military computer industry research and trend analysis report
- OSPF LSA message parsing (under update)
- Ten minutes will take you in-depth understanding of multithreading - multithreaded teamwork: synchronous control
- 浅谈线程池相关配置
- Golang configure export goprivate to pull private library code
- 3124. Word list
猜你喜欢
ZABBIX API creates hosts in batches according to the host information in Excel files
OSPF LSA message parsing (under update)
Addition without addition, subtraction, multiplication and division (simple difficulty)
Special symbols in SAP ui5 data binding syntax, and detailed explanation of absolute binding and relative binding concepts
Baohong industry | what misunderstandings should we pay attention to when diversifying investment
Which kind of sports headphones is easier to use? The most recommended sports headphones
What kind of good and cost-effective Bluetooth sports headset to buy
Baohong industry | 6 financial management models at different stages of life
QT implementation interface jump
Mongodb base de données non relationnelle
随机推荐
【做题打卡】集成每日5题分享(第二期)
[staff] the direction of the symbol stem and the connecting line (the symbol stem faces | the symbol stem below the third line faces upward | the symbol stem above the third line faces downward | the
PMP personal sprint preparation experience
Actual battle of financial risk control - under Feature Engineering
离婚3年以发现尚未分割的共同财产,还可以要么
GB/T-2423. XX environmental test documents, including the latest documents
Query word weight, search word weight calculation
Redis set command line operation (intersection, union and difference, random reading, etc.)
多线程查询,效率翻倍
[question 008: what is UV in unity?]
Baohong industry | what misunderstandings should we pay attention to when diversifying investment
Ten minutes will take you in-depth understanding of multithreading - multithreaded teamwork: synchronous control
Set status bar color
SAP ui5 beginner tutorial 19 - SAP ui5 data types and complex data binding
LFM signal denoising, time-frequency analysis, filtering
使用 useDeferredValue 进行异步渲染
Provincial election + noi Part IV graph theory
A list of job levels and salaries in common Internet companies. Those who have conditions must enter big factories. The salary is really high
Es interview questions
2022-2028 global deep sea generator controller industry research and trend analysis report