2013-06-11 32 views
-1

我寫了下面的類返回狀滾動骰子的隨機數:理解類並且使用隨機

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace GameTest 
{ 
    class Dice 
    { 
    public int publicMinNum 
    { 
     get { return _minNum; } 
     set { _minNum = value; } 
    } 

    public int publicMaxNum 
    { 
     get { return _maxNum; } 
     set { _maxNum = value; } 
    } 

    static int _minNum; 
    static int _maxNum; 

    static Random diceRoll = new Random(); 
    public int rolled = diceRoll.Next(_minNum, _maxNum); 
} 
} 

此類稱爲幾次在我的表格:

private void btnPushMe_Click(object sender, EventArgs e) 
    { 
     Dice myRoll = new Dice(); 
     myRoll.publicMinNum = 1; 
     myRoll.publicMaxNum = 7; 

     lblMain.Text = myRoll.rolled.ToString(); 

     Dice mySecondRoll = new Dice(); 
     mySecondRoll.publicMinNum = 1; 
     mySecondRoll.publicMaxNum = 13; 

     lblMain2.Text = mySecondRoll.rolled.ToString(); 
    } 

正如你所看到的,我給班級打電話myRollmySecondRoll兩次。我認爲通過這樣做,將創建的類和輸出兩個不同號碼(一個1和6之間,另1和12)

我遇到的問題是獨立的實例:

1)所述第一number out總是0.

2)這個類的兩個實例互相干涉,應該在1到6之間的數字不是。

我想知道,不只是如何解決代碼,但也想解釋這裏發生了什麼,爲什麼,謝謝。

+3

問計教程(或其他廣泛的問題)不是建設性的,不適合Stack Exchange站點的Q&A格式,並引發討論。 –

+0

這段代碼有幾個問題。您的整數最小和最大字段不應該是靜態的,您的滾動字段既不應該公開,也不應該按照它的方式進行初始化。但主要是,你是正確的,你需要一個更好的教程,但不幸的是,這不是這個網站的主題。我投票結束,因爲沒有建設性。 –

+0

http://www.blackwasp.co.uk/CSharpClassProperties.aspx – Learner

回答

5

問題是您正在將Dice類中的字段聲明爲static。這意味着該變量只有一個實例,它將在應用程序中的類的所有實例之間共享。

以下行:

public int rolled = diceRoll.Next(_minNum, _maxNum); 

...獲取運行您創建new Dice()的那一刻,這意味着你還沒有初始化,您_minNum_maxNum值尚未:這就是爲什麼它給你一個0 。您可以變成一個屬性,因此代碼將等待被運行,直到你問它:

public int Rolled { get { return diceRoll.Next(_minNum, _maxNum); } } 

...但預計不會一般屬性通過詢問他們的價值只是改變。這種代碼傾向於創建所謂的Heisenbugs,這很難追查,因爲系統的行爲只是通過觀察它而改變。

因此,這裏是你可能會重新寫你的類,使用Roll()方法實際執行軋輥,使代碼繼續檢查的最後一個卷的價值的一種方式,和屬性在必要時:

public class Die 
{ 

    // Using a constructor makes it obvious that you expect this 
    // class to be initialized with both minimum and maximum values. 
    public Die(int minNum, int maxNum) 
    { 
     // You may want to add error-checking here, to throw an exception 
     // in the event that minNum and maxNum values are incorrect. 

     // Initialize the values. 
     MinNum = minNum; 
     MaxNum = maxNum; 

     // Dice never start out with "no" value, right? 
     Roll(); 
    } 

    // These will presumably only be set by the constructor, but people can 
    // check to see what the min and max are at any time. 
    public int MinNum { get; private set; } 

    public int MaxNum { get; private set; } 

    // Keeps track of the most recent roll value. 
    private int _lastRoll; 

    // Creates a new _lastRoll value, and returns it. 
    public int Roll() { 
     _lastRoll = diceRoll.Next(MinNum, MaxNum); 
     return _lastRoll; 
    } 

    // Returns the result of the last roll, without rolling again. 
    public int LastRoll {get {return _lastRoll;}} 

    // This Random object will be reused by all instances, which helps 
    // make results of multiple dice somewhat less random. 
    private static readonly Random diceRoll = new Random(); 
} 

(注意「死」是「骰子」的單數形式)。用法:

private void btnPushMe_Click(object sender, EventArgs e) 
{ 
    Die myRoll = new Die(1, 7); 
    lblMain.Text = myRoll.Roll().ToString(); 

    Die myRoll2 = new Die(1, 13); 
    lblMain2.Text = mySecondRoll.Roll().ToString(); 
} 
+0

謝謝。我有一種感覺可能是,但是由於某種原因,VS急切地希望這些變量是靜態的或者炸彈出來的。我猜測有更好的方法來構建它,解決這個問題? – Chris

+0

@Chris你是從靜態方法還是類調用它? –

+0

@Chris:定義「炸彈爆炸」。最有可能的原因是你試圖從靜態上下文訪問這些字段中的一個。 – StriplingWarrior

1

我會改變你的類看起來更像如下:

class Dice 
{ 
    // These are non-static fields. They are unique to each implementation of the 
    // class. (i.e. Each time you create a 'Dice', these will be "created" as well. 
    private int _minNum, _maxNum; 

    // Readonly means that we can't set _diceRand anywhere but the constructor. 
    // This way, we don't accidently mess with it later in the code. 
    // Per comment's suggestion, leave this as static... that way only one 
    // implementation is used and you get more random results. This means that 
    // each implementation of the Dice will use the same _diceRand 
    private static readonly Random _diceRand = new Random(); 

    // A constructor allows you to set the intial values. 
    // You would do this to FORCE the code to set it, instead 
    // of relying on the programmer to remember to set the values 
    // later. 
    public Dice(int min, int max) 
    { 
    _minNum = min; 
    _maxNum = max; 
    } 

    // Properties 
    public Int32 MinNum 
    { 
    get { return _minNum; } 
    set { _minNum = value; } 
    } 

    public Int32 MaxNum 
    { 
    get { return _maxNum; } 
    set { _maxNum = value; } 
    } 

    // Methods 
    // I would make your 'rolled' look more like a method instead of a public 
    // a variable. If you have it as a variable, then each time you call it, you 
    // do NOT get the next random value. It only initializes to that... so it would 
    // never change. Using a method will have it assign a new value each time. 
    public int NextRoll() 
    { 
    return _diceRand.Next(_minNum, _maxNum); 
    }  
} 
+2

實際上,Random()靜態是好的。你通常只需要一個隨機的實例,以獲得最好的隨機結果。 - 否則當(幾乎)同時創建多個隨機數時,它們的輸出將變爲可預測的 –

+0

同意,並且我更新了我的答案。 – Andrew

+0

您的更新很受歡迎。投票翻轉。 –

1

的GET/setter方法支持字段被標記爲 「static」。如果變量聲明爲「static」,則該值在整個應用程序中保持不變,並在它們所在類型的不同實例之間共享。

請參閱here

此外,

,因爲你的類屬性包含沒有邏輯,我建議使用「automatic」屬性。

class Dice 
    { 
     public int publicMinNum { get; set; } 
     public int publicMaxNum { get; set; } 
     Random diceRoll = new Random(); 
     public int rolled = diceRoll.Next(publicMinNum , publicMaxNum); 
    } 

automatic propertieshere教程。

1

您的問題是由於static成員。

static的MSDN文檔中,「雖然類的實例包含類的所有實例字段的單獨副本,但每個靜態字段只有一個副本。」

5

問題二已經aswered:因爲變量是靜態的:

static int _minNum; 
static int _maxNum; 

問題一,從另一方面心不是回答呢,所以這裏有雲:

public int rolled = diceRoll.Next(_minNum, _maxNum); 

這是不是有些動態調用。這是一個字段初始化,並將在構造函數之前進行設置。你可以通過第一次使用骰子來調試。

在這一點上兩者_minNum_maxNum仍然爲0,所以推出將被設置爲0

這可以通過打開卷成物業也被固定:

public int rolled 
    { 
     get { return diceRoll.Next(_minNum, _maxNum); } 
    } 

目前_minNum_maxNum因爲它們是靜態的而第一次被設置,因此當你創建第二個骰子時,它們已經被設置。

編輯,因爲一個建議是問,這是我想創建它:

骰子

class Dice 
{ 
    private static Random diceRoll = new Random(); 

    private int _min; 
    private int _max; 
    public int Rolled { get; private set; } 

    public Dice(int min, int max) 
    { 
     _min = min; 
     _max = max; 

     // initializes the dice 
     Rolled = diceRoll.Next(_min, _max); 
    } 

    public int ReRoll 
    { 
     get 
     { 
      Rolled = diceRoll.Next(_min, _max); 
      return Rolled; 
     } 
    } 
} 

注意骰子有兩個屬性:RolledReRoll。因爲你的意圖不清楚,我已經添加了兩個來說明行爲。

Rolled由構造函數設置。如果你想要一個新號碼,你可以ReRoll

如果你有意要一個擲骰子的壽命爲每個擲骰子一個(但我不這麼認爲),你會刪除ReRoll方法。

骰子將被稱爲像這樣:

private static void Main(string[] args) 
    { 
     Dice myRoll = new Dice(1, 7); 

     // All the same 
     var result1 = myRoll.Rolled.ToString(); 
     var result2 = myRoll.Rolled.ToString(); 
     var result3 = myRoll.Rolled.ToString(); 

     // something new 
     var result4 = myRoll.ReRoll.ToString(); 

     Dice mySecondRoll = new Dice(1, 13); 
     var result = mySecondRoll.ReRoll.ToString(); 
    } 
+0

迄今爲止,這是比其他答案更好的解決方案,因爲它解決了'滾動'初始化器。然而,它應該是更清晰和獨立的意思是與當前的靜態字段做什麼(明確你的建議),我認爲更好的建議獲取滾動值是爲了(可能是一個方法調用而不是屬性)。 –

+0

即使使用靜態的邏輯不正確並且需要修復,這實際上也可以完美地工作。謝謝你的幫助! – Chris

+0

不客氣。我已經按照@ user414076的要求添加了一個示例 –

1

我覺得這裏真正的問題是,你還沒有完全建模的模具正常。

一個骰子具有最小值和最大值(定義了一個範圍的開始和結束),但是一旦骰子已經制作完畢,您將無法更改該骰子,即六面骰子不會變成八面死。這樣就不需要公共設置者。

現在,並非所有的die都具有相同的範圍,這是特定於每個die的東西,因此這些屬性應該屬於該實例而不是static

同樣,每個die對象都有一個CurrentRoll值,它代表面朝上的數字,這確實是隨機生成的。但要更改模具的CurrentRoll您需要Roll它。

這留下的實現模具看起來像

class Die 
{ 
    private static Random _random; 

    public int CurrentRoll { get; private set; } 

    public int Min { get; private set; } 

    public int Max { get; private set; } 

    public Die(int min, int max) 
    { 
     Min = min; 
     Max = max; 
     Roll(); 
    } 

    public int Roll() 
    { 
     CurrentRoll = _random.Next(Min, Max+1); // note the upperbound is exlusive hence +1 
     return CurrentRoll; 
    } 
} 

,你會使用它像

public static void Main() 
{ 
    Die d1 = new Die(1, 6); 
    Die d2 = new Die(1, 6); 

    Console.WriteLine(d1.Roll()); 
    Console.WriteLine(d2.Roll()); 
    //... 
} 

demo