简体   繁体   中英

c# forms: where do i place my random number generator?

I was trying to use a random number generator in a windows forms app, but I'm having trouble figuring out where to put it. I'm being told "place it in the form constructor" but I don't think we're talking the same language. Here's the code im trying to find a home for:

Random rnd = new Random();
int guessMe = rnd.Next(0,100);

But whenever I try to place it outside of the click even method, it says:

A field initializer cannot reference the non-static field, method, or property 'LAB6B.Form1.r'

So i'm assuming that means what it sounds like; it has to be inside of a static method. But the form constructor doesn't seem to have any static methods either. Can someone toss me a bone?


Here is the code from the the Form1.cs. Im reading some other tutorial online right now that is saying to do the random number generator in the Form1_Load event, but I'm getting a context/scope error from the button click event.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace LAB6B
    {

        public partial class Form1 : Form
        {


            public Form1()
            {
                InitializeComponent();            

            }

            private void Form1_Load(object sender, EventArgs e)
            {
                Random rnd = new Random();
                int guessMe = rnd.Next(0, 100);
            }

            private void btnEvaluate_Click(object sender, EventArgs e)
            {            
                int totGuesses = 0, myGuess;

                if (txtGuess.Text != "")
                {
                    myGuess = int.Parse(txtGuess.Text);
                    totGuesses++;
                    if (myGuess < guessMe)
                    {
                        btnEvaluate.Visible = false;
                        lblResult.Text = "Too Low!!";
                        lblResult.Visible = true;
                        BackColor = Color.SeaGreen;
                    }
                }

            }
        }
    }

If you've been given that advice, someone is probably suggesting that you declare an instance variable, but initialize it in your constructor. For example:

public class Foo
{
    private readonly Random rnd;

    public Foo()
    {
        rnd = new Random();
        // Other construction code
    }
}

Then you can use rnd anywhere in your class.

This is actually mostly equivalent to:

public class Foo
{
    private readonly Random rnd = new Random();

    public Foo()
    {
        // Other construction code
    }
}

... which I mostly prefer, as it shows that the initialization of rnd has nothing to do with any constructor parameters.

As it seems that part of your difficulty is with the guessMe variable, here's a more complete version:

public class Foo
{
    private readonly Random rnd = new Random();
    private int guessMe;

    public Foo()
    {
        guessMe = rng.Next(0, 100);
    }
}

This is assuming you need guessMe to be an instance variable so you can refer to it throughout the class. On the other hand, perhaps you don't need the Random variable to be an instance variable - if you're only generating one random number, it would be better as:

public class Foo
{
    private readonly int guessMe;

    public Foo()
    {
        Random rnd = new Random();
        guessMe = rnd.Next(0, 100);
    }
}

However, personally I wouldn't use either of these approaches. I would use a technique which creates a single instance of Random per thread, to avoid both the perils of Random being non-thread-safe, and the perils of creating two instances of Random very close to each other in time, and ending up with the same seeds.

I've written about this reasonably extensively in an article on my site , including this class:

using System;
using System.Threading;

public static class RandomProvider
{    
    private static int seed = Environment.TickCount;

    private static ThreadLocal<Random> randomWrapper =
        new ThreadLocal<Random>(() =>
            new Random(Interlocked.Increment(ref seed))
        );

    public static Random GetThreadRandom()
    {
        return randomWrapper.Value;
    }
}

You can either use RandomProvider directly (calling GetThreadRandom every time you need to generate a random number) or you can pass RandomProvider.GetThreadRandom into your class as a constructor argument for a parameter of type Func<Random> - ie inject a dependency of "I want to be able to get an instance of Random at any time".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM