当前位置:网站首页>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
边栏推荐
- MongoDB非關系型數據庫
- [staff] pitch representation (treble clef | C3 60 ~ B3 71 pitch representation | C4 72 pitch representation | C5 84 pitch representation)
- 結婚後
- Actual battle of financial risk control - under Feature Engineering
- STM32__ 05 - PWM controlled DC motor
- 多线程查询,效率翻倍
- What is the function of the headphone driver
- CVPR 2022 | Dalian Institute of technology proposes a self calibration lighting framework for low light level image enhancement of real scenes
- Jvm-01 (phased learning)
- What are the common proxy servers and what are the differences?
猜你喜欢
Which kind of sports headphones is easier to use? The most recommended sports headphones
Deployment practice and problem solving of dash application development environment based on jupyter Lab
[learn C and fly] 3day Chapter 2 program in C language (exercise 2.3 calculate piecewise functions)
結婚後
[learn C and fly] 1day Chapter 2 (exercise 2.2 find the temperature of Fahrenheit corresponding to 100 ° f)
OSPF LSA message parsing (under update)
Baohong industry | 6 financial management models at different stages of life
Remote connection to MySQL under windows and Linux system
MongoDB非关系型数据库
使用 useDeferredValue 进行异步渲染
随机推荐
CVPR 2022 | Dalian Institute of technology proposes a self calibration lighting framework for low light level image enhancement of real scenes
实现一个自定义布局的扫码功能
Missing numbers from 0 to n-1 (simple difficulty)
[JS reverse series] analysis of a customs publicity platform
Coordinatorlayout + tablayout + viewpager2 (there is another recyclerview nested inside), and the sliding conflict of recyclerview is solved
Systemserver service and servicemanager service analysis
2022-2028 global manual dental cleaning equipment industry research and trend analysis report
Provincial election + noi Part IV graph theory
2022-2028 global soft capsule manufacturing machine industry research and trend analysis report
Software testing learning notes - network knowledge
Learning notes of software testing -- theoretical knowledge of software testing
Share the basic knowledge of a common Hongmeng application
[learn C and fly] day 5 chapter 2 program in C language (Exercise 2)
Build a modern data architecture on the cloud with Amazon AppFlow, Amazon lake formation and Amazon redshift
Query word weight, search word weight calculation
C return multiple values getter setter queries the database and adds the list return value to the window
The capacity is upgraded again, and the new 256gb large capacity specification of Lexar rexa 2000x memory card is added
Divorce for 3 years to discover the undivided joint property, or
Pat a-1165 block reversing (25 points)
[staff] pitch representation (bass clef | C1 36 note pitch representation | C2 48 note pitch representation | C3 60 note pitch representation)