How to save the state of a Random generator in C#?
For testing purposes I'm creating random numbers with a given seed (i.e. not based on the current time).
Thus the whole program is deterministic.
If something happens, I'd like to be able to quickly restore a point "shortly before" the incident.
Therefore I need to be able to restore a System.Random
to a previous state.
Is there a way to extract a seed which I can use to recreate the random generator?
In line with the answer given here, I wrote a small class to help with saving and restoring the state.
void Main()
{
var r = new Random();
Enumerable.Range(1, 5).Select(idx => r.Next()).Dump("before save");
var s = r.Save();
Enumerable.Range(1, 5).Select(idx => r.Next()).Dump("after save");
r = s.Restore();
Enumerable.Range(1, 5).Select(idx => r.Next()).Dump("after restore");
s.Dump();
}
public static class RandomExtensions
{
public static RandomState Save(this Random random)
{
var binaryFormatter = new BinaryFormatter();
using (var temp = new MemoryStream())
{
binaryFormatter.Serialize(temp, random);
return new RandomState(temp.ToArray());
}
}
public static Random Restore(this RandomState state)
{
var binaryFormatter = new BinaryFormatter();
using (var temp = new MemoryStream(state.State))
{
return (Random)binaryFormatter.Deserialize(temp);
}
}
}
public struct RandomState
{
public readonly byte[] State;
public RandomState(byte[] state)
{
State = state;
}
}
You can test this code in LINQPad.
This is what I came up:
Basically it extracts the private seed array. You just need to be careful to restore an "unshared" array.
var first = new Random(100);
// gain access to private seed array of Random
var seedArrayInfo = typeof(Random).GetField("SeedArray", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var seedArray = seedArrayInfo.GetValue(first) as int[];
var other = new Random(200); // seed doesn't matter!
var seedArrayCopy = seedArray.ToArray(); // we need to copy since otherwise they share the array!
seedArrayInfo.SetValue(other, seedArrayCopy);
for (var i = 10; i < 1000; ++i)
{
var v1 = first.Next(i);
var v2 = other.Next(i);
Debug.Assert(v1 == v2);
}