tags:

views:

62

answers:

2

Hey guys,

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?

+3  A: 

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> - i.e. inject a dependency of "I want to be able to get an instance of Random at any time".

Jon Skeet
I really appreciate the detail. It still isn't really working for me but I think you're a bit above my skill and the scope of what I'm trying to accomplish lol. Its a class assignment to create a number guessing game. So im using the forms and code generated by C#.net, basic file set up; Form1.cs, Form1.Designer.cs, and Program.cs I'm not sure where to make the random number generator work. Is it a complication with partial classes? I'm having scope issues.
Sinaesthetic
@Sinaesthetic: No, it shouldn't be anything to do with being partial - but it's hard to say what's wrong when you haven't provided much context. If you post a short but complete program demonstrating the problem, we could explain things more clearly. (Although I'm unlikely to be posting again tonight.)
Jon Skeet
ok i figured it out based on what you told me.public partial class Form1 : Form { public static readonly Random rnd = new Random(); int guessMe = rnd.Next(0,100); this finally got rid of the error stating that it couldnt be accessed outside of a static method. Everything looks good and the variable is now global :) thanks!
Sinaesthetic
@Sinaeshetic: No, that's not a good way to do it. Static variables holding instances of Random are a bad idea. Make `rnd` an instance variable, but put the `guessMe` initialization in a method or the constructor. I've edited my answer with some examples.
Jon Skeet
A: 

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

            }
        }
    }
Sinaesthetic
@Sinaesthetic: This should be in the question, rather than as an answer.
Jon Skeet