C# 理解类和使用随机变量

C# 理解类和使用随机变量,c#,class,C#,Class,我编写了以下类来返回一个随机数,比如掷骰子: 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; }

我编写了以下类来返回一个随机数,比如掷骰子:

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
mySecondRoll
。我认为这样做会创建单独的类实例,并输出两个单独的数字(一个介于1和6之间,另一个介于1和12之间)

我遇到的问题是:

1) 输出的第一个数字始终为0

2) 该类的两个实例相互干扰,即应该在1和6之间的数字不是


我想知道的不仅仅是如何修复代码,还想解释一下这里发生了什么以及为什么会发生这种情况,谢谢。

问题是您将Dice类中的字段声明为
static
。这意味着该变量只有一个实例,将在应用程序中的所有类实例之间共享

以下一行:

public int rolled = diceRoll.Next(_minNum, _maxNum);
。。。在您创建
新骰子()
时运行,这意味着您尚未初始化
\u minNum
\u maxNum
值:这就是为什么它会给您一个
0
。您可以将其转换为属性,因此代码将等待运行,直到您请求:

public int Rolled { get { return diceRoll.Next(_minNum, _maxNum); } }
。。。但通常情况下,不希望仅仅通过询问其价值来改变属性。这类代码倾向于创建所谓的,很难跟踪的代码,因为系统的行为只是通过尝试观察它而改变

因此,这里有一种方法可以重新编写类,使用
Roll()
方法实际执行Roll,以及一个允许代码在必要时不断检查最后一个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();
}
(请注意,“骰子”是“骰子”的单数形式)。用法:


我会将您的类更改为更像以下内容:

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

您的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 );
    }

自动属性教程

您的问题是由于
静态
成员造成的


从上的MSDN文档中,“虽然类的实例包含该类所有实例字段的单独副本,但每个静态字段只有一个副本。”

问题二已经提出:因为变量是静态的:

static int _minNum;
static int _maxNum;
另一方面,第一个问题尚未得到回答,因此:

public int rolled = diceRoll.Next(_minNum, _maxNum);
这不是什么动态调用。这是一个字段初始化,甚至会在构造函数之前设置。您可以通过第一次调试骰子来检查这一点

此时
\u minNum
\u maxNum
仍为0,因此rolled将设置为0

这也可以通过将Roll转化为属性来解决:

    public int rolled
    {
        get { return diceRoll.Next(_minNum, _maxNum); }
    }
目前,
\u minNum
\u 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;
        }
    }
}
注意,骰子有两个属性:
Rolled
,和
ReRoll
。因为你的意图不清楚,我添加了这两个来说明你的行为

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

我认为这里真正的问题是你没有很好地为模具建模

模具有一个最小值和最大值(定义范围的开始和结束),但一旦模具制作完成,您就无法更改此值,即六面模具不能制作成八面模具。因此,不需要公共设定者

现在,不是所有的骰子都共享相同的范围,这是每个骰子特有的,因此这些属性应该属于实例,而不是

同样,每个模具对象都有一个表示正面朝上的数字的
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());
    //...
}

询问教程(或其他广泛的问题)没有建设性,不符合Stack Exchange站点的问答格式,并导致讨论。此代码存在几个问题。整数min和max字段不应是静态的,滚动字段既不应是公共的,也不应按原样初始化。但最重要的是,你需要一个更好的教程,这是正确的,但不幸的是,这不是本网站的主题。我投票认为这不是建设性的。将标题改为更能描述实际问题会有所帮助。他不仅要求提供教程,还存在一些编码问题。所以,没必要投否决票。谢谢。我有一种感觉,可能是因为某种原因,VS非常希望这些变量是静态的,否则它就会爆炸。我猜有一个更好的方法来构建它来解决这个问题?@Chris你是从静态方法还是类来调用它?@Chris:定义“爆炸”。很可能是因为您试图从静态上下文访问其中一个字段。好的,无论是谁通过并对每个答案投了反对票,都需要解释自己。每个答案都是错误的。当您删除static时,请尝试运行*代码。您的输出是否在范围内?不应该这样。请注意这里的第一条评论。“它是炸弹
public static void Main()
{
    Die d1 = new Die(1, 6);
    Die d2 = new Die(1, 6);

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