Deep clone System.Random

I am trying to deeply clone an object that contains a System.Random variable. My application must be deterministic, so I need to fix the state of a random object. My project is based on .Net Core 2.0.

I use deep cloning code from here ( How do you make a deep copy of an object in .NET (C # specifically)? ) Which uses serialization.

The documentation for System.Random is mixed:

Serializable

Not serializable

and I get the following error.

System.Runtime.Serialization.SerializationException HResult = 0x8013150C Message = Type 'System.Random' in Assembly 'System.Private.CoreLib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = 7cec85d7bea7798e' is not marked as serializable. Source = System.Runtime.Serialization.Formatters

Can System.Random be cloned the way I want?

I created a small program for illustration.

using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace RandomTest { class Program { static void Main(string[] args) { Container C = new Container(); Container CopyC = DeepClone(C); } public static T DeepClone<T>(T obj) { using(var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); //<-- error here ms.Position = 0; return (T)formatter.Deserialize(ms); } } } [Serializable] public class Container { public ObjectType AnObject; public Container() { AnObject = new ObjectType(); } } [Serializable] public class ObjectType { //[NonSerialized] // Uncommenting here removes the error internal System.Random R; } } 

I probably don't need a Container object, but this structure is more like my application.

Creating R [NonSerialized] removes the error, but after deserializing I do not return my random object back. I tried to recreate a random object, but it starts a new random sequence and therefore violates the deterministic requirement.

+5
source share
2 answers

You can use JSON.NET to do this.

Use project | Manage NuGet packages to add "Newtonsoft.Json" (latest stable version 10.0.3) to the project.

Then you can write a Cloner class that uses Json.NET to clone an object:

 public static class Cloner { public static T Clone<T>(T source) { if (ReferenceEquals(source, null)) return default(T); var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() }; return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, settings), settings); } class ContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Select(p => base.CreateProperty(p, memberSerialization)) .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Select(f => base.CreateProperty(f, memberSerialization))) .ToList(); props.ForEach(p => { p.Writable = true; p.Readable = true; }); return props; } } } 

Then you can write code like this:

 var inner = new ObjectType {R = new Random(12345)}; var outer = new Container {AnObject = inner}; var clone = Cloner.Clone(outer); Console.WriteLine(clone.AnObject.R.Next()); // Prints 143337951 Console.WriteLine(outer.AnObject.R.Next()); // Also prints 143337951 
0
source

It seems that you can serialize and deserialize a random class by writing your own serializer and using reflection. Please check .

Here is an example of your code. Of course, he needs refactoring. I just added here to show that it works.

 class Program { static void Main(string[] args) { Container C = new Container(); Console.WriteLine(C.AnObject.R.Next()); Container CopyC = DeepClone(C); Console.WriteLine(C.AnObject.R.Next()); Console.WriteLine(CopyC.AnObject.R.Next()); } public static T DeepClone<T>(T obj) { using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); //<-- error here ms.Position = 0; return (T)formatter.Deserialize(ms); } } } [Serializable] public class Container { public ObjectType AnObject; public Container() { AnObject = new ObjectType { R = new Random() }; } } [Serializable] public class ObjectType : ISerializable { internal Random R; public ObjectType() { } protected ObjectType(SerializationInfo info, StreamingContext context) { R = new Random(); var binaryFormatter = new BinaryFormatter(); using (var temp = new MemoryStream(Encoding.BigEndianUnicode.GetBytes(info.GetString("_seedArr")))) { var arr = (int[])binaryFormatter.Deserialize(temp); R.GetType().GetField("_seedArray", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(R, arr); } R.GetType().GetField("_inext", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(R, info.GetInt32("_inext")); R.GetType().GetField("_inextp", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(R, info.GetInt32("_inextp")); } public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { var binaryFormatter = new BinaryFormatter(); var arr = R.GetType().GetField("_seedArray", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); using (var temp = new MemoryStream()) { binaryFormatter.Serialize(temp, arr.GetValue(R)); info.AddValue("_seedArr", Encoding.BigEndianUnicode.GetString(temp.ToArray())); } info.AddValue("_inext", R.GetType().GetField("_inext", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(R)); info.AddValue("_inextp", R.GetType().GetField("_inextp", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(R)); } } 
0
source

Source: https://habr.com/ru/post/1273976/


All Articles