2016-08-23 99 views
0

为了确保不同的随机数,您应该只使用Random类的一个实例,如答案hereherehere中的建议。C#在使用库时生成随机数,使用内部随机实例

在我的库中,我需要随机数,所以我创建了一个类Randomizer,它提供了一些方法,它使用单个Random实例返回随机数。下面是Randomizer代码的片段:

class Randomizer 
{ 
    private Randomizer() { } 
    public static Randomizer Instance { get; } = new Randomizer(); 

    private static readonly Random random = new Random(); 
    private static readonly object syncLock = new object(); 

    public int Next(int minValue, int maxValue) 
    { 
     lock(syncLock) 
     { 
      return random.Next(minValue, maxValue); 
     } 
    } 

    // rest of code 
} 

现在,如果我的媒体库的用户还需要什么样的随机数?我是否应该将我的Randomizer类公开并在库的文档中指定,该用户应该使用我的Randomizer类来生成随机数字?

+3

你至少应该提供一个构造函数,一个'Random'实例 –

+1

什么将随机数被用于? – Jonathan

+0

如果我提供了一个采用“Random”实例的构造函数,我将不得不在库的其他位置创建一个Random实例。那么我应该创建一个公用的(出库)属性或方法,它返回这个实例吗?或者,在使用任何其他库类之前,库的用户应该创建“Random”实例并将其传递给“Randomizer”构造器**? @Matt我知道它,但在'Random.Next(int,int)'方法中,第二个参数也被称为maxValue @Jonathan我将使用它们来检查,如果随机数小于某些指定的值。 – Darko

回答

2

为了保证不同的随机数,你应该使用一个Random类

这是不完全正确的只有一个实例。创建Random的多个实例是完全没问题的,只要你没有在一个紧密的循环中初始化它们(因为它们是由当前时间播种的,所以你希望当前时间在实例化时各不相同) 。

当你创建一个只创建一个Random对象并重用它的静态类时,没关系。理论上你的图书馆的调用者可以创建自己创建的静态Random,因此它们的生成器会获得与你的相同的种子,并且具有相同的随机数序列。那可能将不会发生,因为它需要创建静态类。它也可能无论如何不会影响,除非有一个原因为什么调用者的序列和你的序列之间的相关性很重要。

0

为什么你用自己的实现为图书馆的消费者加班?

如果它的内部,因为你需要一个非常具体的随机提供者为您的图书馆的内部运作。如果没有必要让外部消费者也遵守您在与您的图书馆互动时在代码中强加的规则,那么请不要暴露您的生成器,并让用户在他需要时使用他自己的Random。另一方面,如果您需要消费者在与库的公共API进行交互时使用特定的随机生成器,那么您需要公开暴露该类。

+0

没有必要让conumer使用我的特定随机生成器。但是,如果消费者创建了自己的“随机”实例,那么由两个“随机”实例(我的,在库和消费者的内部)生成的数字是否真的是随机的?我读过,我应该只使用一个“Random”实例(请参阅问题中的链接)。 – Darko

+1

@Darko。不,这绝对没有问题。参见[Matthew Strawbridge](http://stackoverflow.com/users/241605/matthew-strawbridge)[answer](http://stackoverflow.com/a/39100001/767890),他解释了在创建时会出现什么潜在问题多个“随机”实例。 – InBetween

0

通过使用确保为随机(例如从操作系统获取一个)的显式种子初始化您的Random对象。这样,用户是否创建自己的Random实例并不重要,您不需要考虑将调用与用户代码的Next方法同步。

但是,如果你无法获得一个随机种子,一个解决办法是定义一个接口,让你的库的用户注入的情况下,以需要随机数的每个组件:

public interface IRandomizer 
{ 
    int Next(int minValue, int maxValue); 
} 

合同的接口必须定义库的确切需求(例如,Next方法必须是线程安全的)。 您的Randomizer类可能会实现此接口以用作默认实现。

例如一键生成库组件可以提供两个构造器:

public class KeyGenerator 
{ 
    private IRandomizer randomizer; 

    public KeyGenerator(IRandomizer randomizer) 
    { 
     // instance will use the specified IRandomizer 
     this.randomizer = randomizer; 
    } 

    public KeyGenerator() 
    { 
     // instance will use your Randomizer class. 
     this.randomizer = Randomizer.Instance; 
    } 
} 

这样库的用户可以决定他是否要提供自己的随机数生成器或使用磁带库的默认。

0

我会用下面的办法:

public interface IRandomizer 
{ 
    int Next(int inclusiveLower, int exclusiveOuter); 
} 

然后我会公开一个静态类,其默认实现和可选的工厂方法来创建特定的“播种”的实例。需要注意的是内部使用的发电机(Default)是公众入店,但它可能非常清楚是内部唯一的:

//Note, the following code is an outline of the general idea 

public static class RandomGenerator 
{ 
    private readonly static Lazy<Randomizer> inner = new Lazy<Randomizer>(); 
    public static IRandomizer Default { get { return inner.Value; } } 

    public static IRandomizer CreateNew(Random seed) 
    { 
     return new Randomizer(seed); 
    } 

    private class Randomizer : IRandomizer 
    { 
     private readonly Random random; 
     private readonly object syncLock = new object(); 

     public Randomizer(Random seed = null) 
     { 
      random = seed ?? new Random(); 
     } 

     public int Next(int minValue, int maxValue) 
     { 
      lock (syncLock) 
      { 
       return random.Next(minValue, maxValue); 
      } 
     } 
    } 
}