-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();
    }

ご覧のとおり、クラスをmyRollおよびとして 2 回呼び出しますmySecondRoll。これを行うことで、クラスの個別のインスタンスが作成され、2 つの個別の数値 (1 から 6 の間、もう 1 つは 1 から 12) が出力されると考えました。

私が抱えている問題は次のとおりです。

1) 最初の数字は常に 0 です。

2) クラスの 2 つのインスタンスが互いに干渉します。1 から 6 の間であるはずの数値は、そうではありません。

コードを修正する方法だけでなく、ここで何が起こっているのか、そしてその理由についても説明したいと思います。ありがとうございます。

4

6 に答える 6

5

質問 2 は既に答えられています。変数が静的であるためです。

static int _minNum;
static int _maxNum;

一方、質問1はまだ答えられていないので、次のようになります。

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

これは動的呼び出しではありません。これはフィールドの初期化であり、コンストラクターの前でも設定されます。これは、最初にサイコロを使ってデバッグすることで確認できます。

その時点で_minNum_maxNumはまだ 0 なので、ロールは 0 に設定されます

これは、ロールをプロパティに変えることでも修正できます。

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

現時点_minNum_maxNumは、 と は静的であるため、最初に設定されています。そのため、2 番目のサイコロを作成すると、すでに設定されています。

編集、推奨事項が尋ねられたので、これが私がそれを作成する方法です:

サイコロ

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;
        }
    }
}

サイコロには と の 2 つのプロパティがあることに注意してRolledくださいReRoll。あなたの意図が不明確であるため、動作を説明するために両方を追加しました。

Rolledコンストラクターによって設定されます。新しい番号が必要な場合は、ReRoll.

サイコロごとにロールの寿命を意図的に 1 つにしたい場合 (しかし、私はそうは思いません)、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();
    }
于 2013-06-11T21:23:37.760 に答える
5

問題は、Dice クラスのフィールドを として宣言していることですstatic。これは、その変数のインスタンスが 1 つだけ存在し、アプリケーション内のクラスのすべてのインスタンスで共有されることを意味します。

次の行:

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

... を作成した瞬間に実行されます。これは、との値new Dice()をまだ初期化していないことを意味します。これをプロパティに変えることができるので、コードは要求されるまで実行を待機します。_minNum_maxNum0

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

...しかし、通常、値を要求するだけでプロパティが変更されることは期待されていません。この種のコードは、いわゆるハイゼンバグを作成する傾向があります。これは、システムの動作が観察しようとするだけで変化するため、追跡が非常に困難です。

Roll()したがって、メソッドを使用して実際にロールを実行し、必要に応じてコードが最後のロールの値をチェックし続けることができるようにするプロパティを使用して、クラスを書き直す方法の 1 つを次に示します。

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();
}

(「die」は「dice」の単数形であることに注意してください)。使用法:

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();
}
于 2013-06-11T21:11:50.260 に答える
1

ここでの本当の問題は、Die を適切にモデル化していないことだと思います。

サイコロには最小値と最大値 (範囲の開始と終了を定義する) がありますが、一度サイコロを作成すると、これを変更することはできません。つまり、6 面のサイコロを 8 面のサイコロにすることはできません。そのため、パブリック セッターは必要ありません。

現在、すべてのダイが同じ範囲を共有しているわけではありません。これは各ダイに固有のものであるため、これらのプロパティはインスタンスに属し、staticではありません。

ここでも、各サイコロ オブジェクトにCurrentRollは表向きの数字を表す値があり、これは実際にはランダムに生成されます。ただしCurrentRoll、サイコロの を変更するには、Rollそれに必要です。

これにより、次のような Die の実装が残ります

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());
    //...
}

デモ

于 2013-06-11T22:20:32.743 に答える
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);
  }    
}
于 2013-06-11T21:12:20.233 に答える
1

get/setter バッキング フィールドは " static" としてマークされています。変数が " " と宣言されている場合static、その値はアプリケーション全体で保持され、それらが存在する型の異なるインスタンス間で共有されます。

ここを参照してください。

また、

クラス プロパティにはロジックが含まれていないため、" 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 properties ここのチュートリアル。

于 2013-06-11T21:12:36.720 に答える
1

あなたの問題はstaticメンバーによるものです。

staticに関する MSDN ドキュメントから、「クラスのインスタンスにはクラスのすべてのインスタンス フィールドの個別のコピーが含まれますが、各静的フィールドのコピーは 1 つだけです。」

于 2013-06-11T21:12:58.847 に答える