locked
Define constants in C#

    Question

  • Hello

    I am brand new to C# and this is my first attempt, so terminology and best practices may not be all there.

    I am trying to write a game, with a controlling class called Game in a file called Game.cs. Having done some investigation, I have created some structure to the rest of the project. I have another class called Cell in Cell.cs in a sub folder called Cell. What I want to do is define global constants, so that they can be used across the project (#DEFINE in C). I have created an additional class called Constants in Constants.cs in the sub folder Constants. It contains the following :-

    namespace <some namespace>.Constants
    {
        public class Constants
        {
            // Container for Global Constants
            public const int GRID_SIZE = 81;
            public const int ELEMENT_SIZE = 9;
            public const int BOX_SIZE = 3;

            // Private constructor to prevent instantiation of, or inheritance
            // from, this class.
            private Constants()
            {
            }
        }
    }

    In Cell.cs I have defined the following :-

    namespace <some namespace>.Cell
    {
        public class Cell
        {
            private bool locked;        // Indicates whether this cell can be modified.
            private int answer;         // Contains the value of the answer for this cell or zero.

            private bool[] possibles = new bool[ELEMENT_SIZE];

            And then loads more code...............

    When I build this I get an error for the line that commences private bool[] possibles...... and that error is CS0103 The name ELEMENT_SIZE does not exist in the current context.

    So please help. What am I doing wrong. I have trawled around the web for hours looking for solutions and they all give this idea as how to do this and say it works, but not for me. I am using VS Express 2008 on Vista based PC.

    Any help gratefully received.

    Cheers

    PSO57




    Tuesday, March 17, 2009 4:37 PM

Answers

  • pso57 said:

    People - This seems like a bit of a mess on my part and an awful lot of trouble to do something that is quite simple in C. How on earth do people define global constants in C#, like this or am I going about this in the wrong way. 


    For a good while you were confused about namespaces (which is not just a C# feature, it exists in native C++ too). The current trouble is you switched from a static class with const int fields to using an enum. So my answer to you assumed the former - perhaps my fault. When you use enums in C#, you need to cast them to int as required - though a compiler provided implicit cast may have been helpful here.

    http://blog.voidnish.com
    • Marked as answer by pso57 Tuesday, March 17, 2009 7:40 PM
    Tuesday, March 17, 2009 7:06 PM

All replies

  • Use :

            private bool[] possibles = new bool[Constants.ELEMENT_SIZE];

    Also, you can make the class static :

        public static class Constants
        {
            // Container for Global Constants
            public const int GRID_SIZE = 81;
            public const int ELEMENT_SIZE = 9;
            public const int BOX_SIZE = 3;
        }

    Now you won't need the private constructor.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 4:43 PM
  • Creating enums is another way to create constants.  It is typically how it is done in C#. 

        /// <summary>  
        /// Container for Global Constants  
        /// </summary>  
        enum Constants  
        {  
            //  Do not duplicate values.  
            GRID_SIZE = 81,  
            ELEMENT_SIZE = 9,  
            BOX_SIZE = 3  
     
        } 


    It might time to unlearn some of your C programming habits and patterns. 

    You might want to look deeper into OOP and Design Patterns.
    Mark the best replies as answers. "Fooling computers since 1971."
    Tuesday, March 17, 2009 4:59 PM
  • Thank you, I had already tried that and it does not work. Error CS0234 The type or namespace name ELEMENT_SIZE does not exist in the namespace <some namespace> (are you missing an assembly reference?).  Before anybody asks, I am using a valid namespace, I just doctored it for this forum.

    But I will use the static aspect of your answer.

    Thanks.

    PSO57
    Tuesday, March 17, 2009 5:01 PM
  • Ok so you use enums, but the same question applies, where do you define them to be global and am I likely to run into the same problem regardless of changing to enums.

    PSO57
    Tuesday, March 17, 2009 5:07 PM
  • Changed it to use enums, but still in the Constants.cs file and I am back to error CS0103, the original error. I need these constants available to multiple classes, that's the crux of the matter, so is there a better way of doing this.
    Tuesday, March 17, 2009 5:18 PM
  • pso57 said:

    Thank you, I had already tried that and it does not work. Error CS0234 The type or namespace name ELEMENT_SIZE does not exist in the namespace <some namespace> (are you missing an assembly reference?).  Before anybody asks, I am using a valid namespace, I just doctored it for this forum.

    But I will use the static aspect of your answer.

    Thanks.

    PSO57


    That's because the constants are defined in a class in a different namespace. Either fully qualify the namespace or use an using-directive to specify that namespace.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 5:30 PM
  • pso57 said:

    Changed it to use enums, but still in the Constants.cs file and I am back to error CS0103, the original error. I need these constants available to multiple classes, that's the crux of the matter, so is there a better way of doing this.


    Whetehr you use enums or a static class with constant fields, you still need to either fully qualify the type name with the namespace or use an using-directive.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 5:31 PM
  • That maybe true, but it does not get rid of error CS0234, what is an assembly reference?
    Tuesday, March 17, 2009 5:46 PM
  • To define a global enum that all classes can use, you can do the following:

    1namespace [your namespace
    2
    3     internal enum Constants 
    4     { 
    5          GRID_SIZE = 81, 
    6          ELEMENT_SIZE = 9, 
    7          BOX_SIZE = 3 
    8     } 
    9


    All classes that exist in the same namespace as the one defined will be able to use this enum.

    There are 10 types of people in this world, those who understand Binary, and those who don't.
    Tuesday, March 17, 2009 5:52 PM
  • That is the way it is defined I am afraid and it still gives error CS0103. I have tried 2 styles of namespace. Say the overall game namespace is xyz, I have tried all files with that namespace, or the way they are generated as xys.Constants in Constants.cs, xyz.Cell in Cell.cs and xyz in Game.cs. It still doesn't work.


    Tuesday, March 17, 2009 6:00 PM
  • Post code of the file containing the enum, and the file that generates the error.
    Mark the best replies as answers. "Fooling computers since 1971."
    Tuesday, March 17, 2009 6:04 PM
  • pso57 said:

    That maybe true, but it does not get rid of error CS0234, what is an assembly reference?


    The missing assembly reference is just a compiler guess (this time it's wrong).

    The Constants class is in the namespace <some namespace>.Constants

    The Cell class where you are trying to access it is in <some other namespace>.Cell

    So you cannot directly use Constants from the Cell class without specifying the full namespace.

    Instead of :

    private bool[] possibles = new bool[Constants.ELEMENT_SIZE];

    You need :

    private bool[] possibles = new bool[<full namespace here>.Constants.ELEMENT_SIZE];

    Alternatively, inside the 2nd namespace, you can use an using directive :

    namespace <some namespace>.Cell
    {
      using <full namespace containing Constants>;

    Now, inside the <some namespace>.Cell scope, you can access any type inside the <full namespace containing Constants> namespace.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 6:06 PM
  • Rudedog2 said:

    Post code of the file containing the enum, and the file that generates the error.


    Mark the best replies as answers. "Fooling computers since 1971."



    and make sure both files are in the same project AND namespace.
    Mark the best replies as answers. "Fooling computers since 1971."
    Tuesday, March 17, 2009 6:06 PM
  • As you will see from the attached files, I have hardly started this project before running into this problem. I only started this afternoon and have spent a good deal of that time trying to find a solution.  I have attached the 3 files and they are all defined in a project called Sudoku. First is Game.cs, then Constants.cs and finally Cell.cs. I have tried your latest idea in these and still the same error CS0103.

    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel; 
    using System.Data; 
    using System.Drawing; 
    using System.Text; 
    using System.Windows.Forms; 
     
    namespace Sudoku 
        public partial class Game : Form 
        { 
            public Game() 
            { 
                InitializeComponent(); 
            } 
        } 
     

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
     
    namespace Sudoku.Constants 
    //    public static class Constants 
    //    { 
    //        // Container for Global Constants 
    //        public const int GRID_SIZE = 81; 
    //        public const int ELEMENT_SIZE = 9; 
    //        public const int BOX_SIZE = 3; 
    //    } 
     
        // Container for Global Constants 
        internal enum Constants 
        { 
            GRID_SIZE = 81, 
            ELEMENT_SIZE = 9, 
            BOX_SIZE = 3 
        } 
     

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
     
    namespace Sudoku.Cell 
        using Sudoku.Constants; 
     
        public class Cell 
        { 
            private bool locked;        // Indicates whether this cell can be modified. 
            private int answer;         // Contains the value of the answer for this cell or zero. 
            private bool[] possibles = new bool[ELEMENT_SIZE]; 
            // An array of 9 bool's which indicate the possible values this cell 
            // can have, based on other cells which have been solved. 
     
            // Constructor 
            public Cell() 
            { 
                locked = false
                answer = 0; 
     
                for (int i = 0; i < possibles.Length; i++) 
                { 
                    possibles[i] = true
                } 
            } 
     
            // Property for getting the value of the answer. 
            public int Answer 
            { 
                get 
                { 
                    return answer; 
                } 
            } 
     
            // Methods 
     
            // Indicate if cell is locked. 
            public bool IsLocked() 
            { 
                return locked; 
            } 
     
            // Lock the cell with the supplied value. Used as part of the game initialisztion. 
            public void SetAnswerLocked(int answer) 
            { 
                locked = true
                SetAnswerLocked(answer); 
            } 
     
            // Set the answer for the current cell to the supplied value, if it is valid. 
            public void SetAnswer(int answer) 
            { 
                if ((answer < 0) || (answer > 9)) 
                { 
                    throw new ApplicationException( 
                        "Class Cell SetAnswer supplied with invalid answer " + answer); 
                } 
     
                this.answer = answer; 
                for (int i = 0; i < possibles.Length; i++) 
                { 
                    possibles[i] = false
                } 
            } 
        } 
     



    Tuesday, March 17, 2009 6:39 PM
  • Posted code in reply to the other person helping, if you want to have a look.
    Tuesday, March 17, 2009 6:40 PM
  • pso57 said:

    Posted code in reply to the other person helping, if you want to have a look.


    Yeah, but the code is still wrong :

    Change :

    private bool[] possibles = new bool[ELEMENT_SIZE];

    to :

    private bool[] possibles = new bool[Constants.ELEMENT_SIZE];

    While you have "using Sudoku.Constants;" on top, that's only the namespace. You still need to specify the enum-name.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 6:44 PM
  • That gives a different error now :-

    Error    1    Cannot implicitly convert type 'Sudoku.Constants.Constants' to 'int'. An explicit conversion exists (are you missing a cast?)    C:\AA\DEV\VS C Sharpe\Sudoku\Sudoku\Cell\Cell.cs    14    45    Sudoku

    Tuesday, March 17, 2009 6:54 PM
  • People - This seems like a bit of a mess on my part and an awful lot of trouble to do something that is quite simple in C. How on earth do people define global constants in C#, like this or am I going about this in the wrong way. 
    Tuesday, March 17, 2009 7:03 PM
  • pso57 said:

    That gives a different error now :-

    Error    1    Cannot implicitly convert type 'Sudoku.Constants.Constants' to 'int'. An explicit conversion exists (are you missing a cast?)    C:\AA\DEV\VS C Sharpe\Sudoku\Sudoku\Cell\Cell.cs    14    45    Sudoku


    That's because of using enums - you now need a cast as follows :

    private bool[] possibles = new bool[(int)Constants.ELEMENT_SIZE];

    Had you stuck to the static class with const int fields, you wouldn't need the cast.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 7:04 PM
  • pso57 said:

    People - This seems like a bit of a mess on my part and an awful lot of trouble to do something that is quite simple in C. How on earth do people define global constants in C#, like this or am I going about this in the wrong way. 


    For a good while you were confused about namespaces (which is not just a C# feature, it exists in native C++ too). The current trouble is you switched from a static class with const int fields to using an enum. So my answer to you assumed the former - perhaps my fault. When you use enums in C#, you need to cast them to int as required - though a compiler provided implicit cast may have been helpful here.

    http://blog.voidnish.com
    • Marked as answer by pso57 Tuesday, March 17, 2009 7:40 PM
    Tuesday, March 17, 2009 7:06 PM
  • I agree.  Seeing how it is being used, a class would be better.  Beware of having the same label for a class and a namespace.
    Mark the best replies as answers. "Fooling computers since 1971."
    Tuesday, March 17, 2009 7:14 PM
  • Thank you, that works, but it still seems to me (with my C hat on still) one hell of a lot of trouble just to use constants.

    Thanks to everyone for there help.

    PSO57
    Tuesday, March 17, 2009 7:14 PM
  • Can you extrapolate that last sentence a little.

    Thanks.

    Tuesday, March 17, 2009 7:16 PM
  • pso57 said:

    Thank you, that works, but it still seems to me (with my C hat on still) one hell of a lot of trouble just to use constants.

    Thanks to everyone for there help.

    PSO57


    Yeah, your code design is more C-like than C#-like. That's one reason why you think it's a lot of work. When moving from C to C# you need to do a mental design shift from structural to OO programming. There may be some overlap depending on how extreme you are in your OO approach but it's still a major thought shift.

    http://blog.voidnish.com
    Tuesday, March 17, 2009 7:19 PM
  • pso57 said:

    Thank you, that works, but it still seems to me (with my C hat on still) one hell of a lot of trouble just to use constants.

    Thanks to everyone for there help.

    PSO57


    Now that you've figured out the constants, let me add one thing that wasn't mentioned yet. 

    It's typically a good practice to avoid public constants in C#. 

    The reason for this is the way the compiler handles public constants.  If you have a separate assembly that references the assembly that defines a public constant, and that separate assembly uses the constant, the value from the constant will be hard-coded into the separate assembly.  This means that when the constant is changed at some point, the separate assembly will also have to be rebuilt while referencing the newly altered version, or it's runtime behavior will still act as though the value in the constant was the original constant.  For example:

    1. I define Assembly A.  In it, I define the constant "WIDGET_SIZE" and I set it's value to 24.
    2. I create Assembly B, and reference Assembly A. In it, I use WIDGET_SIZE for one of my methods.
    3. I deploy the application to my client.
    4. Later, the size of the widget is to change, from 24 to 25.  I alter the value in Assembly A, rebuild Assembly A (but not Assembly B), and then deploy Assembly A.
    5. Later, my client calls me angry.  The Widget sizes aren't right half the time.  This happens to be because the value "24" was literally hard-coded into Assembly B, which wasn't rebuilt, while all the references to WIDGET_SIZE in Assembly A are listed as 25.

    The moral of the story is: if you're going to use constants, do yourself a favor and limit them to extreme scenarios, such as pi, or some other value that is very unlikely to change.  Otherwise, it's a better practice to use static readonly class-level values to get the same effect.

    David Morton - http://blog.davemorton.net/
    Tuesday, March 17, 2009 7:42 PM
  • Extrapolate?  Sure.  Doing this will get you into trouble at some point.

    namespace <some namespace>.Constants
    {
        public class Constants
        {
            // Container for Global Constants
            public const int GRID_SIZE = 81;
            public const int ELEMENT_SIZE = 9;
            public const int BOX_SIZE = 3;

            // Private constructor to prevent instantiation of, or inheritance
            // from, this class.
            private Constants()
            {
            }
        }
    }

    Mark the best replies as answers. "Fooling computers since 1971."
    Tuesday, March 17, 2009 8:13 PM
  • This is good and sound advice and I appreciate it. But, in the scenario you have suggested, if your client discovers the bug, then I would suggest that your regression test packs are not up to par, in that they do not contain tests for extreme limits.  That's a testing issue as much as a design issue.

    Thank you.
    Wednesday, March 18, 2009 9:47 AM
  • Agreed this may create problems, I think I have seen this in some fo the examples from the book I read, but as yet I do not understand why.
    Wednesday, March 18, 2009 9:52 AM
  • pso57 said:

    Agreed this may create problems, I think I have seen this in some fo the examples from the book I read, but as yet I do not understand why.



    namespace PatternDemo  
    {  
        class Class5  
        {  
            void method()  
            {  
                Constants.MyClass obj = new Constants.MyClass(); // which type???  
            }  
        }  
    }  
    namespace PatternDemo.Constants  
    {  
        public class Constants  
        {  
            // Container for Global Constants  
            public const int GRID_SIZE = 81;  
            public const int ELEMENT_SIZE = 9;  
            public const int BOX_SIZE = 3;  
     
            // Private constructor to prevent instantiation of, or inheritance  
            // from, this class.  
            private Constants()  
            {  
            }  
            internal class MyClass  
            {  
            }  
        }  
        internal class MyClass  
        {  
        }  
     
     


    Which type is being created in method() of Class5?  Generics can also get wishy washy, too.  Just avoid the practice altogether.  The second definition of MyClass is being referenced.  Now add this to the code.  It will confuse the compiler.  Note the error.


    namespace PatternDemo  
    {  
        class Constants  
        {  
              
        }  

    But, what if this code appeared in a different assembly from the code above, DLL1 and DLL2.  Each assembly would happily compile.  TROUBLE BEGINS.  Now suppose a client references both DLL1 and DLL2.  A  compiler error similar to the one from above will result.


    Mark the best replies as answers. "Fooling computers since 1971."
    Wednesday, March 18, 2009 1:16 PM