Proper way to implement global constants plus memory considerations
-
Thursday, May 03, 2012 8:01 PM
I am new to Visual Studio and Visual Basic, but I am a revived dBase, FoxPro, Visual FoxPro, ColdFusion programmer... I ain't as good as I once was, but I'm as good once as I ever was...
I am programming in a team environment...
So I am looking for how to implement some constants for the three main reasons anyone would want to use global constants: Readability, Maintenance, Memory Usage. For example in a list of error return codes, I may assign "File Not Found" an error code of 1010.
Return ERROR_FILE_NOT_FOUND vs
Return 1010
The code is easier to read and only has to be changed in one place if we decide to re-align our codes.
However, compiling this, I need to ensure that ERROR_FILE_NOT_FOUND is replaced with 1010 when compiled and not using precious memory like a variable would. I have searched quite extensively for this info and cannot find it.
So Q1 is: Can someone direct me to documentation that shows constants do not take memory when running the program because they are replaced with the values when compiled?
Q2 is WHERE should these be declared? I have seen MANY places (in MSDN) say global constants need to be declared in the "Declarations Section" of the class, like this...
Scope of User-Defined Constants
A Const statement's scope is the same as that of a variable declared in the same location. You can specify scope in any of the following ways:
- To create a constant that exists only within a procedure, declare it within that procedure.
- To create a constant available to all procedures within a class, but not to any code outside that module, declare it in the declarations section of the class.
- To create a constant that is available to all members of an assembly, but not to outside clients of the assembly, declare it using the Friend keyword in the declarations section of the class.
- To create a constant available throughout the application, declare it using the Public keyword in the declarations section [of] the class.
and this:
Global Scope
When a variable or constant is declared as global it is visible to all procedures and modules that comprise the application. Global variables and constants, as with Module level variables, must be declared outside of any procedures in the Declarations section and must use the Public keyword.
The language above stating "Global variables and constants, as with Module level variables..." insinuates to me that Global Constants and Module Level constants are different, thus having different scopes.
And I have yet to see ANYWHERE that tells me to put stuff in the Declarations Section of a class or program or application, tell me just WHERE the declarations section actually IS!
They always give the code of how to declare (I can do this...) but they don't say where the code GOES.
I know I am overlooking the simplest of things, but just trying to be very sure of what I am doing...
Here is where I set them...
Module GlobalModule
' Error Definitions
Public Const ERROR_TEST_IS_BAD As Integer = 1234
' Return Codes
Public Const RETURN_SUCCESS As Integer = 1000 ' Success for new development
Public Const RETURN_FAILURE As Integer = -1 ' Failure for all applications
End Module
SO again, Q1 is: Can someone direct me to documentation that shows constants do not take memory when running the program because they are replaced with the values when compiled?
Q2 is WHERE should these be declared? I am using a Module... I don;t even reference it (GlobalModule) but it still appears to work.
TIA! (Thanks in Advance)
All Replies
-
Thursday, May 03, 2012 9:11 PM
I am no expert with MSIL (the virtually compiled langague that the .net VM runs) but based on this document:
http://msdn.microsoft.com/en-us/magazine/cc300489.aspx
it appears that constants and values are treated identically in MSIL.
eg:
private startValue as integer = 5
and
Private Const startValue As Integer = 5
both compile to:
ldc.i4.5
in MSIL. as I understand the assembly, ldc = "LoaD Constant" and i4 means a 4byte (32bit) integer. 5 is the value.
the document does say that when initially setting the constant, it does get hard coded into the assembly, so in that regard the initial loading of the constant is minutely faster, but every reference to that constant will still involve an evaluation of the reference, so an 'ldloc' call is still required to access it.
Keep in mind, .Net is a very very high level langague, and is well above concerns like saving a DWord or two of space on a constant declaration. in VB, constants are more about constraining the value to be unchangable rather than a compiler optimization.
good luck, and if you find a better source on this, please post it back. youve gotten me curious.
Ps, as for Q2, I usually add a class with nothing but shared members, including const ints, enums, and static structures. it has worked well for me over the years.
public class Constants public shared AppID = 12 public shared AppName = "Endotherm'''s App" public enum status Idle = 0 Running = 1 Paused = 3 Aborting = 4 Complete = 5 End Enum End Class
- Edited by Endotherm Thursday, May 03, 2012 9:16 PM
- Edited by Endotherm Thursday, May 03, 2012 9:21 PM
- Marked As Answer by Shanks ZenMicrosoft Contingent Staff, Moderator Wednesday, May 30, 2012 7:52 AM
-
Thursday, May 03, 2012 9:43 PMModerator
Hiya Sarge and thanks for the friendly introduction. :) Welcome to the MSDN forums!
For Q1 I'm not sure where to start looking for such a specific piece of information in the documentation... and it may not exist.
I think that is pretty much what a constant implies; it is a fixed, named reference to a value to make code more readable.
I did just create a test app with a constant and then checked the compiled MSIL and even though the source code uses the constant more than once, the actual name can only be found in the initial declaration - so it doesn't exist anywhere else in the MSIL. Is that enough to show that the compiled code is referencing a pointer the value? (not the variable name, or the value itself)
Now, Q2 is the trickier one because there probably is no hard answer.
For a truly global constant or variable you declare a public field or constant in a module. When you do it this way you can just reference any public module members willy-nilly from any of your code files. LOL As that wording might imply, this isn't the best OOP practice because you loose some structure.
There is a strong argument to say that there is no such thing as a "global" value; that is, all values belong to something. In your example above, these would be error codes which should belong to an appropriately named ErrorCode Enum. You wind up with the same effect; the named values are declared once in the enumeration and then your code simply refers to the enum values. And this can be considered even more readable than global constants because the error code value is preceeded by the category (enum name) it belongs to.
In cases where an enum is not appropriate, say for Strings or floating point numbers, then you may once again use a Constant. But to maintain the same structure and encapsulation as you would get from an enum, you can declare this constant in its own sealed, abstract class. Then you can refer to the ClassName.ConstantMember in your code. Its the same effect as with the module, except that you have to type ClassName. before the actual constant name.
You can also use shared members on the class to make any kind of object global (not just types supported by the Constant keyword).
The following kinda shows some examples of this:
Public Class Form1 Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim test As ExecutionErrorCodes = ExecutionErrorCodes.Success If test = ExecutionErrorCodes.Success Then Me.BackgroundImage = Globals.ErrorImage Me.Text = Globals.WelcomeMessage End If End Sub End Class Public NotInheritable Class Globals Public Const WelcomeMessage As String = "Hello World!" Public Shared ReadOnly SomeMaximum As Integer = 42 Public Shared ReadOnly ErrorImage As Image = SystemIcons.Error.ToBitmap Protected Sub New() End Sub End Class Public Enum ExecutionErrorCodes Success = 0 [Error] = 1 End Enum
I don't believe that there is much difference between Constants and Shared members as far as overall performance goes. Someone else might be able to elaborate on that though.Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
- Marked As Answer by Shanks ZenMicrosoft Contingent Staff, Moderator Wednesday, May 30, 2012 7:52 AM
-
Thursday, May 03, 2012 11:02 PM
I urge you to use Enums and put them in a module file. Enum's help you recover the mnemonic in a way that integer constants cannot (see sample below with ToString). You can have several enums if you wish, or you can have one big enum for your original problem (with distinct values unlike the sample where code2=oops2=2). You can cast them to integer (because they are declared as integer), and you can cast an integer back to the enum (assuming/hoping you know what you are doing). Because they are declared 'as integer', code using them will be tight. No downside IMO.
Public Enum ReturnCode As Integer code1 = 1 code2 = 2 End Enum Public Enum IllogicalSituation As Integer oops2 = 2 oops3 = 3 End Enum Public Sub test() Dim x1 As ReturnCode = ReturnCode.code2 Dim x2 As IllogicalSituation = IllogicalSituation.oops2 Dim s1 As String = x1.ToString ' produces "code2" Dim s2 As String = x2.ToString ' produces "oops2" End Sub
-
Friday, May 04, 2012 4:49 PM
You guys are awesome! You have introduced me to enumerations as they relate to constants! MSDN had this:
Constants vs. Enums.
http://msdn.microsoft.com/en-us/library/3sd4y2w7(v=vs.80).aspx
Now, I have looked on the constant page:
http://msdn.microsoft.com/en-us/library/cyxe49xw(v=vs.80).aspx
That is where I would think memory usage would be as I look for complete information on Constants and Enumerations on MSDN...
And so now I'm thinking.... I like how the Enumerations handle constants, very OOP... but, how are they processed? Like variables in memory or like constants hard wired at compilation time. Again the question of memory footprint and speed of execution. We are a performanced based shop with thousands of hits at a time. Maybe nothing compared to some apps, but still significant to us.
Maybe some people with HEAVY usage experience can enlighten us?
Anyone know if Enumerations are in the memory allocations at runtime or hardwired at compile time?
TIA!
- Edited by Sgt. Bob Friday, May 04, 2012 4:50 PM
-
Friday, May 04, 2012 7:27 PM
I made an enum and assigned an enum variable a value. I did the same for an integer. The IL coded up the same - and ldc and a stloc (see below). An enum as integer codes the same as an integer constant. So, you shouldn't worry about footprint and/or execution speed with enum vs integer constants.
FYI, I think your concern in this area is unlikely to be of consequence. If you have performance problems in your system, they won't be related to an issue like enum vs integer. You may have a design issue - too many or too few threads, poor file/database access, no caching when it is appropriate, too much IPC, and so on. You may have a programming issue - string concatenation vs StringBuilder, box/unbox costs, early vs late binding, and so on. Keeping your footprint small is helpful, the main issue being the 'working set', but this will not be affected by a choice like enum vs integer constant.
.method public instance void TestAbc() cil managed { // Code size 5 (0x5) .maxstack 1 .locals init ([0] int32 i, [1] valuetype Test.Form1/abc z) IL_0000: ldc.i4.1 IL_0001: stloc.1 IL_0002: ldc.i4.1 IL_0003: stloc.0 IL_0004: ret } // end of method Form1::TestAbc -
Friday, May 04, 2012 7:40 PM
Hi,
It is hardcoded at compile time. See http://msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx which is the disassembly tool Endotherm talked about. Then if you write a console app that writes the value of an enum - (int)MyEnum.A, not just MyEnum.A in which case it writes the A name - you'll see that the underlying value is just hardcoded.
If you think about it, it makes senses as a constant or an enum is nothing else than a symbolic name for an underlying value that never changes.
That said IMO you should likely look at other places. It's true that you have some low level mechanism that are interesting if you want to max out performance (for example when array bound checks can be optimized etc...) but you should perhaps question first other points. I'm not sure what your app does but for example if it hits a database, respond to numerous http queries, use data that could be cached etc... you'll likely have potential bottlenecks that matters much than basic language constructs (it seems it could be a web app ?).
Here it seems to me you are assuming a bit quickly that the bottlenecks will be caused by basic low level language constructs when it's likely won't be there.
Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
- Edited by Patrice ScribeMVP Friday, May 04, 2012 7:43 PM
- Edited by Patrice ScribeMVP Friday, May 04, 2012 7:44 PM
- Edited by Patrice ScribeMVP Friday, May 04, 2012 7:44 PM
-
Friday, May 04, 2012 8:06 PM
Returning error codes isn't the .Net way - we use exceptions instead.
Constants are replaced in the executable with the value, the value is compiled in as a literal, as if you just typed in the value.
One problem with this is that you have to be certain that you are Never going to change the value if other code is referencing yours and using the constant. For example, if microsoft decided to change the value of Integer.MaxValue, then all the .Net code out there that uses this constant would need to be recompiled so that the new value gets used. Thankfully, unless someone miscounted, Integer.MaxValue isn't going to change. You should use Readonly fields if this could be an issue.
-
Friday, May 04, 2012 8:07 PMModerator
Keep in mind that you are now working in managed code... which in many respects means "don't worry about it!" =P
I don't think you could every find a measurable difference between declaring 10 constants of type integer and declaring an enumeration with 10 members. They both represent immutable values defined at design time.
If you really really have to know how the framework handles this internally, you will probably want to ask in the CLR Forum or perhaps somewhere on TechNet.
I think one thing that most everyone here with heavy usage experience will tell you is that your usage of constants versus enums is probably the very last thing you would be looking for if you found your code had a performance issue. Assuming that .Net is suited to the task at hand, then if your code is slow your candidates will be type conversions, string manipulation, and overall application architecture. When you know all those are correct, you'll look at object handling and make sure that you are disposing things properly and generally cleaning up after yourself as required. If it's still slow, you'll then start looking for all of the possible "Oh, I didn't know .Net could do that"s. And there could be bunches of them... After you're sure you've taken advantage of every possible .Net shortcut you can start to wonder about odd things like if memory management is doing something else that you didn't expect. But chances are you'll never get that far because the application will be working just dandy after one of the more likely problems is found and corrected.
As for memory footprint, again it is managed code and there are potentially other frameworks (application or entity framework for instance) so there is some overhead involved... a "Hello World!" windows forms project has about a 15MB working set before you add anything to it or have it do any measurable work. So that goes back to point #1 - is .Net the appropriate technology for the application?
I guess my point is not that you shouldn't be interested in how the framework actually works - because it is great that you are and it is worth knowing. My point is that you shouldn't let it bog you down too much as you're making the transition into modern managed code. You probably should start by taking for granted that you don't have to worry about memory management. Just learn what the basic rules are for object instantiation and garbage collection and then use common sense. As you get deeper into it, some of the questions will begin to answer themselves; especially if you make friends with F1 and press it whenever the text editor cursor is on something you wonder about. =)
The whole evolution of .net is centered on the concept of abstracting the low-level programming from the programmer and allowing them to concentrate on the actual functionality of their program. We are to the point now where we can write a series of simple classes, marked with attributes and bound through a single template-like class and then generate an entire application, including user forms (web) and underlying SQL database. Forget not worrying about memory management... you didn't worry about database design or creation, data access layers, UI layers, etc. You wrote a single object model, marked it up appropriately, wrote a container object for your model and then generated the rest. There may be a few other steps involved but for many scenarios application development can become just that slick, and provide tons of features like cross-platform ready services, unit-testing ready objects, and a robust LINQ-driven data layer exposed as your strongly typed object model.
I guess the final point here is that after a while you just get used to letting .Net take care of as much heavy lifting as possible. =)
Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
-
Friday, May 04, 2012 8:11 PMModerator
Returning error codes isn't the .Net way - we use exceptions instead.
Constants are replaced in the executable with the value, the value is compiled in as a literal, as if you just typed in the value.
One problem with this is that you have to be certain that you are Never going to change the value if other code is referencing yours and using the constant. For example, if microsoft decided to change the value of Integer.MaxValue, then all the .Net code out there that uses this constant would need to be recompiled so that the new value gets used. Thankfully, unless someone miscounted, Integer.MaxValue isn't going to change. You should use Readonly fields if this could be an issue.
Ooh careful my friend - exceptions are expensive and sometimes a result code enum is the way to go. (granted, we tend to call them result codes cause they are good bad and ugly lol). Its just important that the code be documented to explain its functionality.
Otherwise, great reasoning for favoring shared readonly fields over constants.
Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
-
Friday, May 04, 2012 8:41 PMExceptions are very expensive performance-wise, and that is (certainly) the OP's concern. Also, MS guides against using exceptions as part of normal program flow, and that is (I think) the OP's concern.
-
Friday, May 04, 2012 9:14 PM
I am no expert with MSIL (the virtually compiled langague that the .net VM runs) but based on this document:
http://msdn.microsoft.com/en-us/magazine/cc300489.aspx
it appears that constants and values are treated identically in MSIL.
eg:
private startValue as integer = 5
and
Private Const startValue As Integer = 5
both compile to:
ldc.i4.5
in MSIL. as I understand the assembly, ldc = "LoaD Constant" and i4 means a 4byte (32bit) integer. 5 is the value.
the document does say that when initially setting the constant, it does get hard coded into the assembly, so in that regard the initial loading of the constant is minutely faster, but every reference to that constant will still involve an evaluation of the reference, so an 'ldloc' call is still required to access it.
Keep in mind, .Net is a very very high level langague, and is well above concerns like saving a DWord or two of space on a constant declaration. in VB, constants are more about constraining the value to be unchangable rather than a compiler optimization.
good luck, and if you find a better source on this, please post it back. youve gotten me curious.
Ps, as for Q2, I usually add a class with nothing but shared members, including const ints, enums, and static structures. it has worked well for me over the years.
public class Constants public shared AppID = 12 public shared AppName = "Endotherm'''s App" public enum status Idle = 0 Running = 1 Paused = 3 Aborting = 4 Complete = 5 End Enum End Class
WRONG.
The code is optimized with "lcd.i4.5", but the memory is still used by the value
Look at the MSIL class
The variable "a" was declare here Private a as integer = 5
and the memory was used
{ .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field private static class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.WeakReference> __ENCList .field private class [System.Windows.Forms]System.Windows.Forms.Button _Button1.custom instance void [mscorlib]System.Runtime.CompilerServices.AccessedThroughPropertyAttribute::.ctor(string) = ( 01 00 07 42 75 74 74 6f 6e 31 00 00 ) .field private int32 a .field private class [System]System.ComponentModel.IContainer components .property instance class [System.Windows.Forms]System.Windows.Forms.Button Button1() .method private specialname rtspecialname static void .cctor () cil managed .method public specialname rtspecialname instance void .ctor () cil managed .method public instance void aaaaaa () cil managed .method family strict virtual instance void Dispose ( bool disposing ) cil managed .method private instance void InitializeComponent () cil managed }and here Private Const a as integer = 5
and again the memory was used
.class public auto ansi beforefieldinit WindowsApplication4.Form1 extends [System.Windows.Forms]System.Windows.Forms.Form { .custom instance void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.DesignerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) .field private static class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.WeakReference> __ENCList .field private class [System.Windows.Forms]System.Windows.Forms.Button _Button1.custom instance void [mscorlib]System.Runtime.CompilerServices.AccessedThroughPropertyAttribute::.ctor(string) = ( 01 00 07 42 75 74 74 6f 6e 31 00 00 ) .field private static literal int32 a = int32(5) .field private class [System]System.ComponentModel.IContainer components .property instance class [System.Windows.Forms]System.Windows.Forms.Button Button1() .method private specialname rtspecialname static void .cctor () cil managed .method public specialname rtspecialname instance void .ctor () cil managed .method public instance void aaaaaa () cil managed .method family strict virtual instance void Dispose ( bool disposing ) cil managed .method private instance void InitializeComponent () cil managed
And finaly, if you use Enum, you do create an instance of the class System.Enum ... Takes more memory: I wont post it, but it is 2771 lines of IL code
-
Friday, May 04, 2012 9:22 PM
grumpy today?
well, I just passed on the document, and summarize what it says. if you don't like what it says, take it up with MS.
-
Friday, May 04, 2012 10:05 PM
May be a bit grumpy,
I hate to see some document from Microsoft that say something, and then when when you look at the code generated by your application, you see something different..
Nothing about you,... Sorry if it sounds like that
Luc
- Edited by CrazypennieMicrosoft Community Contributor Friday, May 04, 2012 10:07 PM
-
Saturday, May 05, 2012 1:48 AMno prob, as for the enums, you make a good point. I may have to rethink my use of them, but on my team, keeping folks from adding "magic numbers" everywhere is important as well, since we have to maintain the darn thing for the next decade or so. As I get older I place more emphasis on clear self-documenting code, and sometimes performance takes a little hit as a result.
-
Saturday, May 05, 2012 10:42 AM
Often a small class that contains a few public constant will do as good of enum and use less memory. But sometime it is better to use Enum than having a code difficult to understand.
In todays computer, a few Kb of memory is nothing, using it to avoid code like this:
City = 5734 instead of City = FrenchCities.Paris
is certainly the way to go, It make the difference between a code easy to read and a code almost impossible to read or understand.

