May 24, 2004 12:05 AM

Constant Comprehension

Understanding C# Constants
DevConnections
Rating: (0)

C# 101

LANGUAGES: C#

ASP.NET VERSIONS: All

 

ConstantComprehension

UnderstandingC# Constants

 

 

Popquiz: What's the difference between these three declarations? And, moreimportantly, when should you use each one?

 

private constint _Millenium = 2000;

 

private staticreadonly

  DateTime _classCreation = DateTime.Now;

 

private readonlyDateTime _InstanceTime = DateTime.Now;

 

Thefirst creates a compile-time constant, the second creates a run-time classconstant, and the third creates a run-time object constant. While developing atypical program you'll use all three constructs, so it pays to understand thedifference. This article will explain the differences between these threeconstructs and show you when to use each.

 

Compile-timeConstants

Let'sbegin with compile-time constants. The symbols you define for compile-timeconstants are replaced with the value of the constant at compile time.Therefore this construct:

 

if (myDateTime.Year == _Millenium)

 

compilesto the same IL as if you had written:

 

if (myDateTime.Year == 2000)

 

Thecompiler replaces the symbol with the value of the constant. This is the mainpoint to compile-time constants: These symbols don't exist in the IL, only inyour C# source. Once compiled, you have the same IL as if you had used thenumeric constants in your code.

 

Thisimplementation of compile-time constants places other restrictions on declaringconstants and assigning values to them. First, compile-time constants can onlybe used effectively for primitive types, enums, or strings. Primitive types arethe built-in integral and floating-point types. These are the only types thatallow you to assign meaningful constant values as part of the initializationprocess.

 

The onlyconstant value you can assign to reference types is null. The reason for this restriction is that you cannot use the new operator when you assign a constantvalue. In other words, the following construct won't compile:

 

private constDateTime _classCreation =

   newDateTime(2000, 1, 1, 0, 0, 0);

 

Inpractice, this restricts us to value types and strings. Any other referencetype must be null. User-definedvalue types simply won't work at all. For example:

 

struct MyStruct

{

  // ...

}

 

private staticconst MyStruct _s;   // Doesn't compile.

 

So, acompile-time constant can only be used for primitive types. The IL generatedfor a compile-time constant contains the value, not the symbol. The value is"burned in" at compile time.

 

readonlyValues

readonly values are also constants, in thatthey cannot be modified after the constructor has executed. readonly values are different, however,in that they're set at run time. You have much more flexibility in working withrun-time constants. For one thing, run-time constants can be of any type; aslong as you can assign them in your constructors, they will work. I could make readonly values from DateTimestructures; I could not create DateTime values with const.

 

Secondly,you can use readonly values forinstance constants, storing different values for each instance of a class type.As we saw at the start of this article, the value of _instanceTime isdifferent for every instance of the object being created.

 

The mostimportant distinction is that readonlyvalues are resolved at run time. The IL generated when you reference a readonly constant reference the readonly variable, not the value.

 

Decisions,Decisions

The maindifference between const and readonly fields is in theirflexibility. Suppose you've defined both constand readonly fields in an assembly namedInfrastructure:

 

public classUsefulValues

{

   public static readonlyUsefulInteger = 5;

   public const AnotherUsefulInteger = 10;

}

 

Then, inan assembly named Application you reference those values:

 

for (inti = UsefulValues.UsefulInteger;

     i < UsefulValues.AnotherUsefulInteger;i++)

  Console.WriteLine("value is {0}",i);

 

If yourun your little test, you see the following obvious output:

 

Value is 5

Value is 6

...

Value is 9

 

Timepasses and you release a new version of the Infrastructure assembly with thefollowing changes:

 

public classUsefulValues

{

   public static readonlyUsefulInteger = 105;

   public const AnotherUsefulInteger = 120;

}

 

Youdistribute the Infrastructure assembly without rebuilding your Applicationassembly. What do you suppose happens?

 

You'llget no output at all. The loop now uses the value 105 for its start, and 10 forits end condition. The const valueof 10 was placed into the Application assembly by the C# compiler. Contrastthat with the UsefulInteger value. It was declared as readonly; it gets resolved at run time. Therefore, the Applicationassembly makes use of the new value without even recompiling the Applicationassembly. Simply installing an updated version of the Infrastructure assemblyis enough. The point here is that updating the value of a public constant isreally an interface change. Updating the value of a readonly constant is easily upgradeable.

 

Using readonly constants will also generate asmaller assembly. Every time you use a constvalue, the compiler inserts the value of the constant. When you reference a readonly value, the compiler referencesthat symbol. Repeatedly storing the actual values in the IL will result in alarger assembly than repeatedly referencing the same location. This particularargument does not apply to strings because .NET replaces duplicate stringsusing a process called string interning. The result is that const strings generate more or less thesame IL as readonly strings.

 

Arethere any advantages to using constover readonly? Yes; constants can beused in places where readonly valuescannot, namely attributes. You can use constvalues as the parameters to attribute constructors; you cannot use readonly values, or variables. So, whenyou define objects to use when constructing attributes, those values used forattribute parameters must be const; readonly doesn't work. Figure 1 showsan example of a simple attribute to tag classes with their state.

 

[AttributeUsage(AttributeTargets.Class)]

public classClassStateAttribute : Attribute

{

   [Flags]

   public enum CodeState

  {

    Experimental = 0x01,

    Stable = 0x02,

    Released = 0x04

  }

   public const CodeState Release2Upgrade =

    CodeState.Released |CodeState.Experimental;

 

   public readonly CodeState TheState;

 

   publicClassStateAttribute (CodeState s)

  {

    TheState = s;

  }

}

 

[ClassState(ClassState.Release2Upgrade)]

public classNewCode

{

  // Etc.

}

Figure1: A simpleattribute to tag classes with their state.

 

Youcould not, however, rewrite it using readonlyvalues, as shown in Figure 2.

 

[AttributeUsage(AttributeTargets.Class)]

public classClassStateAttribute : Attribute

{

   [Flags]

   public enum CodeState

  {

    Experimental = 0x01,

    Stable = 0x02,

    Released = 0x04

  }

  // Won't work:   Only constant values can be used.

  // Not read only.

   public static readonlyCodeState Release2Upgrade =

    CodeState.Released |CodeState.Experimental;  

 

   publicClassStateAttribute(CodeState s)

  {

    TheState = s;

  }

}

 

[ClassState(ClassState.Release2Upgrade)]

public classNewCode

{

  // Etc.

}

Figure2: Readonly values won't work on oursimple example.

 

The readonly type doesn't work toinitialize an attribute. The actual value of the object must be available atcompile time for the attribute to get created correctly. Therefore, only valuesdeclared as const (or enums) can beused in this instance.

 

I alwaysget questions about the relative performance of const and readonlyvalues. Frankly, I've never been able to measure any difference between thetwo; for any operation I've tried, they are equivalent. The table in Figure 3summarizes the different use cases I've discussed, and offers myrecommendations.

 

Usage

readonly

const

Comments and Recommendations

Primitive constant

Yes

Yes

Use const. Primitive types that will never change should be const.

Release Dependent const, primitive type

Yes

Yes

Use static readonly. Any constant value that might change should be readonly, not const.

Other constants

Yes

No

Use static readonly. It's the only one that works.

Immutable members

Yes

No

Use (instance) readonly. It's the only option, and immutability is enforced by the compiler.

Enumerated values

No

Yes

Enumerated values must be const.

Values used to construct attributes

No

Yes

These must be constants.

Figure3: Recommendationsfor the use cases discussed in this article.

 

Conclusion

Thereare some small performance gains to be realized from using const instead of readonly,but you give up quite a bit of flexibility. You'll need to recompile everyassembly that uses a const value. Inthe case of readonly you need onlyupdate the definition. This flexibility greatly overrides the minimalperformance gains from using constas the key. Minimize your use of constto attribute parameters and enums. Everything else should be declared readonly instead.

 

Bill Wagner began developing commercial software in1986. Bill founded SRT Solutions, a firm that specializes in advancing softwaredevelopment. He began writing magazine articles in 1992. He wrote the C# Core Language LittleBlack Book (Paraglyph Publishing, 2001) and is currently writing Effective C# forAddison-Wesley. Bill is the Microsoft Regional Director for Michigan. ContactBill at mailto:wwagner@srtsolutions.com.

 

 

 

 

Add a Comment

There are no comments to display. Be the first one!
You must log on before posting a comment.

Are you a new visitor? Register Here

advertisement




Comments from the DevConnections Community

Join our community of development pros.

Windows problem

I all, I have a problem on my Windows Vista that began afetr the purchase of an external Hard Disk Freecom. A few days afetr the purchase I discon...

Most Recent Posts

GOOGLE LINKS
SPONSORED LINKS
FEATURED LINKS