2016-08-23 101 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); 
      } 
     } 
    } 
}