为什么序列化类中的Random实例重复生成相同的数字集?

时间:2011-09-28 20:45:45

标签: c# serialization random

所以我是一个c#noob。我有一个简单的问题,我找不到其他任何地方的答案。

 [Serializable()]
 public class Dictionary
 {
    private Random m_RandomGenerator = new Random();

    public int GetNext() 
    {
        return m_RandomGenerator.Next(100);
    }
 }

每次程序启动时都会加载Dictionary实例,此代码每次运行时都会返回完全相同的序列。我的意思是,每次运行可执行文件。当然它所播种的时间值应该是不同的(DateTime.Now.Ticks我假设?)。

有几点:

  • 只有一个Dictionary实例,在启动时从先前导出的文件中反序列化。
  • 如果我将m_RandomGenerator设为静态,那么问题就解决了。

有谁知道为什么?我仔细检查过我每次都没有创建一个新的Dictionary实例,所以这不是问题。


好吧,让我感到尴尬。事实证明,罪魁祸首是[Serializable()]属性。我使用的字典类是从先前导出的文件加载的,显然是将种子加载回Random()。将变量更改为静态意味着不再从先前序列化的实例加载种子值 - 隐藏问题。

感谢所有人提供建设性意见!

5 个答案:

答案 0 :(得分:6)

(CW,因为这对评论来说太大了)

此种测试只会在种子随机播种时重复进行。你应该发布调用Dictionary的代码,因为那里可能有一些可疑的东西(假设发布的代码是实际的代码)或甚至更好发布你自己的测试来重现问题。

[Test]
public void TestDictionary()
{
    var dictionary = new Dictionary();

    for(int i = 0; i < 10; i++)
    {
        Console.WriteLine(dictionary.GetNext());
    }
}

[Serializable]  // added after the fact
public class Dictionary
{
    //private Random m_RandomGenerator = new Random(12);
    private Random m_RandomGenerator = new Random();

    public int GetNext()
    {
        return m_RandomGenerator.Next(100);
    }
}

此测试重复您的搜索结果,但这是因为答案here

[Test]
public void TestDictionary2()
{
    var alpha = new Dictionary();
    var bravo = new Dictionary();

    for(int i = 0; i < 10; i++)
    {
        Console.WriteLine("{0} - {1}", alpha.GetNext(), bravo.GetNext());
    }
}

为了完整性,这是一个序列化测试:

[Test]
public void SerializationPerhaps()
{
    var charlie = new Dictionary();
    Dictionary delta = null;

    // Borrowed from MSDN:  http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx

    //Opens a file and serializes the object into it in binary format.
    using (var stream = File.Open("data.xml", FileMode.Create))
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(stream, charlie);
    }


    //Opens file "data.xml" and deserializes the object from it.
    using (var stream = File.Open("data.xml", FileMode.Open))
    {
        var formatter = new BinaryFormatter();

        delta = (Dictionary) formatter.Deserialize(stream);
        stream.Close();
    }

    for(int i = 0; i < 10; i++)
    {
        Assert.AreEqual(charlie.GetNext(), delta.GetNext());
    }
}

答案 1 :(得分:1)

您的问题的来源必须是您发布的代码中的其他地方。以下是嵌入测试工具中的相同代码:

using System;

namespace RandomTest
{

    public class Dictionary
    {
        private Random m_RandomGenerator = new Random();

        public int GetNext()
        {
            return m_RandomGenerator.Next(100);
        }
    } 


    class Program
    {
        static void Main(string[] args)
        {
            Dictionary d = new Dictionary();

            for (int i=0;i<10;i++)
            {
                int r = d.GetNext();
                Console.Write("{0} ",r);
            }
            Console.WriteLine();

        }
    }
}

每次运行时都会返回不同的序列。

答案 2 :(得分:1)

如果没有进一步的代码示例,您的问题的答案很简单。

  

不重复。

我将您的代码示例插入控制台应用程序,结果完全随机。

namespace Sandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary dict = new Dictionary();
            for (int count = 0; count < 100; count++)
                Console.WriteLine(dict.GetNext());
            Console.ReadLine();
        }
    }

    public class Dictionary
    {
        private Random m_RandomGenerator = new Random();

        public int GetNext()
        {
            return m_RandomGenerator.Next(100);
        }
    }
}

结果:

http://www.codetunnel.com/content/images/random.jpg

答案 3 :(得分:0)

我不知道这是你想要的,但这有助于我解决我的问题。 你的帖子和其他答案,评论带给我这个。

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializeRandom
{
    [Serializable]  // added after the fact
    public class RandomGenerator
    {
        const string fileName = "random.bin";
        private Random random = new Random();

        public int GetNext()
        {
            return random.Next(100);
        }

        public static void Save(RandomGenerator obj)
        {
            using (var stream = File.Open(fileName, FileMode.Create))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
            }
        }

        public static RandomGenerator Load()
        {
            RandomGenerator randomGenerator = null;

            //create new object if file not exist
            if (!File.Exists(fileName))
            {
                randomGenerator = new RandomGenerator();
            }
            else
            {
                //load from bin file
                using (var stream = File.Open(fileName, FileMode.Open))
                {
                    var formatter = new BinaryFormatter();

                    randomGenerator = (RandomGenerator)formatter.Deserialize(stream);
                    stream.Close();
                }
            }

            return randomGenerator;
        }

    }

    }

和测试类

using System.Collections.Generic;
using NUnit.Framework;

namespace SerializeRandom
{
    public class RandomGeneratorTest
    {
        [Test]
        public void TestDictionary1()
        {
            var randomGenerator = RandomGenerator.Load();

            var randomResult1 = new List<int>();
            for (int i = 0; i < 10; i++)
            {
                randomResult1.Add(randomGenerator.GetNext());
            }
            RandomGenerator.Save(randomGenerator);

            randomGenerator = RandomGenerator.Load();
            var randomResult2 = new List<int>();
            for (int i = 0; i < 10; i++)
            {
                randomResult2.Add(randomGenerator.GetNext());
            }

            CollectionAssert.AreNotEqual(randomResult1, randomResult2);

        }

    }
}

答案 4 :(得分:-3)

那是因为(Random())是伪随机数生成器。

需要种子来初始化随机数生成器,使其不重复。

可接受的选择是将计算机时间用作种子。

但是,由于时钟具有有限的分辨率,因此使用无参数构造函数以紧密连续的方式创建不同的随机对象会创建随机数生成器,从而生成相同的随机数序列。

http://msdn.microsoft.com/en-us/library/system.random.aspx

我要小心没有参数的默认构造函数。