Register  
Thursday, March 11, 2010
Blogs  


Latest Blogs
  
ActiReq Preview
  
Latest Articles
  
Archive
  
Featured Blogs
  
02

The readonly modifier is used to declare a constant data member in a class, but providing more flexibility than using the const modifier. The const modifier implicitly defines the data member as static, and must be initialized along with its declaration, as follows:

public const int myConst = 5;

If the const is not initialized along with the declaration, the compiler will generate an error.

Moreover, a const data member must be initialized with a compile-time constant – in other words there is no way to initialize a constant using a runtime value. For instance, the following code doesn’t compile:

public class MyClass
{
	public static string nonConstValue = "This is a variable initialized at runtime";
	public const string constValue = nonConstValue;		// Compilation error
	...
}

as the compiler will inform that “The expression being assigned to 'MyClass.constValue' must be constant”.

The advantage of defining a data member as const is that it cannot be changed – the drawback being the inability to initialize the field in a different place other than the declaration.

On the other hands, the readonly modifier extends the const modifier by allowing the data member to be initialized in any class constructor. This allows a data member to be initialized when a class is being instantiated, preventing it from being modified in any other class method – in other words, once the const data member is initialized it never changes during the class instance lifetime. As opposed to the const modifier, the readonly modifier creates an instance-specific data member, so each class instance will have its own readonly data member instances. This doesn’t mean that a readonly data member cannot be static – simply it must explicitly be declared as such.

All the following declarations and initializations are valid:

public class MyClass
{
	public readonly int _myIntField = 5;	// Allowed
	public readonly string _myStringField;	// Allowed

	public MyClass()
	{
		_myIntField = 8;									// Allowed
		_myStringField = "Initialized in the constructor";	// Allowed
	}

	public static void Main()
	{
		MyClass myClass;
		
		myClass = new MyClass();
		
		System.Console.Out.WriteLine(string.Format("myIntField = {0}", myClass._myIntField));
		System.Console.Out.WriteLine(string.Format("myStringField = {0}", myClass._myStringField));
	}
}

The member declared at line 4 is initialized in the constructor with a string constant, whereas the int member declared at line 3 is initialized both in the declaration and in the constructor. In this case, the constructor initialization takes precedence, as it is executed after the data member instantiation.

The following case instead is not valid

public class MyClass
{
	public readonly string _myStringField;	// Allowed

	public MyClass()
	{
		Init();
	}

	private void Init()
	{
		_myStringField = "Cannot be initialized here";		// Compilation error
	}
	
	...	
}

That’s because the readonly member is initialized in a class member which is not a constructor, even if this method is called from the constructor. The reason why this is not allowed is that the Init() method can be called from anywhere within the class, or even outside if it is protected, internal or public rather than private.

Another advantage of the readonly modifier is that it can be initialized with a runtime value or an expression evaluated at runtime, for instance:

public class MyClass
{
	public readonly int  _myIntField;	// Allowed

	public MyClass(int runtimeValue)
	{
		_myIntField = 10 * runtimeValue;
	}

	
	public static void Main()
	{
		MyClass myClass;
		
		myClass = new MyClass(5);
		
		System.Console.Out.WriteLine(string.Format("myIntField = {0}", myClass._myIntField));
	}
}

As we can expect, the _myIntField readonly data member will be initialized to 10 * 5 = 50.

Readonly data members are not limited to basic data types – it can be used for class instances. For example, when writing a database accessor class we might want to provide a database connection:

public class MyDatabaseAccessor
{
	private readonly SqlConnection _connection;
	
	public MyDatabaseAccessor (SqlConnection connection)
	{
		_connection = connection;
		...
	}
	
	...

}

public class MyApp
{
	public static void Main()
	{
		SqlConnection connection = new SqlConnection();
		
		// Initialize the connection
		...
		
		MyDatabaseAccessor myAccessor = new MyDatabaseAccessor(connection);
		
		// Do something
		...
	}
}

The above code would work without declaring the _connection data member as readonly – but in such case the connection can be changed anywhere in the class.

It’s important to keep in mind that when a readonly data member is of a reference type (i.e. instance of a class and not either a struct or a base data type) the reference is constant, but the instance it points to is not. So the internal status of a readonly instance can change and can be changed, as proven by the following sample:

// Class with a data member
public class MyClassType
{
	private string _internalStatus;
	
	public string InternalStatus {
		get {return _internalStatus;} 
		set {_internalStatus = value;}
	}
}

// Class with a readonly data member
public class MyClassWithReadonlyMember
{
	private readonly MyClassType _readonlyInstance;
	
	public MyClassWithReadonlyMember(string value)
	{
		_readonlyInstance = new MyClassType();
		_readonlyInstance.InternalStatus = value;
	}
	
	public void SetStatus(string message)
	{
		_readonlyInstance.InternalStatus = message;
	}
	
	public void OutputStatus()
	{
		System.Console.Out.WriteLine(string.Format(
				@"Internal status = '{0}'", _readonlyInstance.InternalStatus));
	}
}

public class MyApp
{
	public static void Main()
	{
		MyClassWithReadonlyMember classInstance;
		
		// Create an instance of MyClassWithReadOnlyMember class
		// The instance of MyClassType is initialized to the default 
		// value provided in the constructor
		classInstance = new MyClassWithReadonlyMember("Initial value");
		classInstance.OutputStatus();
		
		// The internal status of the readonly data member of 
		// MyClassWithReadonlyMember is changed
		classInstance.SetStatus("New Value");
		classInstance.OutputStatus();		
	}
}

If this code is compiled and run. it will generate the following output:

Internal status = 'Initial value'
Internal status = 'New Value'

 

2009.01.30 Addendum

Thanks to what Chris Marisic highlighted in his comments below, I need to clarify a few points to prevent some misleading assumptions.

#1 - readonly members can be modified using reflection. The readonly modifier is a compiler directive, so any attempt to modify a readonly data member is detected during compilation. A consequence is that using reflection it's possible to modify a readonly data member

#2 - I wrote above that the const modifier "allows a data member to be initialized when a class is being instantiated, preventing it from being modified in any other class method". This statement doesn't mean that the data member is immutable, at least not always. .NET supports two kinds of data types: value types and reference types.:

  • value types are allocated on the thread's stack and they hold the actual value
  • reference types are allocated on the thread's stack and they hold a pointer to an object allocated in the managed heap

In both cases variables are allocated on the thread's stack, the difference being that a value type holds the actual value (for example, an integer, a struct), whereas the reference type holds a pointer to the memory area where the actual value is stored (for example, an instance of a StringBuilder class).

When a value type is declared as readonly, its value is immutable, meaning that once assigned it cannot be modified outside constructors.

When a reference type is declared as readonly, the pointer is immutable, but not the object it points to. This means that:

  • a reference type data member can be initialized in order to point to an instance of a class, but once this is done it's not possible to make it point to another instance of a class outside of constructors
  • the readonly modifier has no effect on the object the readonly data member points to.

Maybe a real example helps to better understand the difference. Let's create a simple class and a simple struct. They have one int data member only.

public class MyClass
{
	public int IntField;
}

public struct MyStruct
{
	public int IntField;
}

Now let's use them in a test class:

public class MyTestClass
{
	private readonly MyClass _myClass;
	private readonly MyStruct _myStruct;

	public MyTestClass()
	{
		_myClass = new MyClass();
		_myClass.IntField = 4;

		_myStruct.IntField = 5;
	}
}

The _myClass data member is a reference type, so it must be initialized by allocating a new instance of MyClass and assigning its pointer, as seen at line 8. On the other hand, the _myStruct data member is a value type, meaning that it is already an instance of MyStruct. Initialization of their respective fields are done at lines 9 and 11.

Let's add a method to the MyTestClass class.

public class MyTestClass
{
	private readonly MyClass _myClass;
	private readonly MyStruct _myStruct;

	public MyTestClass()
	{
		_myClass = new MyClass();
		_myClass.IntField = 4;

		_myStruct.IntField = 5;
	}

	public void TestReadonly()
	{
		_myClass.IntField = 7;		// Valid statement
		_myStruct.IntField = 10;	// Error: _myStruct is readonly
		_myClass = null;			// Error: _myClass is readonly
	}
}

If we try to compile this code, we get 2 errors:

MyTest.cs(26,3): error CS1648: Members of readonly field 'MyTestClass._myStruct'
        cannot be modified (except in a constructor or a variable initializer)


MyTest.cs(27,3): error CS0191: A readonly field cannot be assigned to (except in
        a constructor or a variable initializer)

MyStruct is a struct, hence a value type; _myStruct is immutable since declared as readonly, so any of its data members cannot be modified outside of a constructor.

MyClass is a class, hence a reference type; _myClass is immutable since declared as readonly, so it cannot be modified in order to point to another instance of a class (or to null) outside of a constructor.

 

del.icio.us Tags: ,,,
Posted in: Development, C#

Post Rating

Comments

Anthony Grace
Sunday, January 25, 2009 10:57 PM
Very good article. I would add that despite being limited to only numbers and strings, compile-time constants are the preferred choice over runtime constants when peformance is a primary issue. Great blog :-)
Andrew Robinson
# Andrew Robinson
Monday, January 26, 2009 12:04 PM
One other factor that can be key at times: A const that is referenced across assemblies is copied locally at compile time to the referencing assembly. If the source later changes and the consumer is not recompiled, the old value will continue to be used. A readonly value is always referenced at runtime so no issue. Bottom line, a const should truely be const forever.
SuperJason
Monday, January 26, 2009 12:23 PM
ReSharper will automatically suggest making fields readonly when appropriate. it's pretty slick.
Anthony Grace
Monday, January 26, 2009 12:40 PM
@Andrew: very good point about the cross-assembly scenario.

@SuperJason: is this going to be included in VS 2010?
Antonio Bello
# Antonio Bello
Monday, January 26, 2009 1:14 PM
I'd really like to see all ReSharper features implemented in Visual Studio.
I think Resharper is the best addin for VS - I couldn't live without. I thought it was too expensive until a friend of mine told me to try it. Then I couldn't do anything else than buying it.

But I'm somehow surprised some features, such as automatic inclusion of namespaces, are not implemented in the current version of visual studio. I also develop in Java using Eclipse, and several of the Resharper features are available in Eclipse. And it's a free product...

Ant.
Antonio Bello
# Antonio Bello
Monday, January 26, 2009 1:42 PM
forgot to say... I knew about the readonly keyword thanks to ReSharper :)

Andrew, thanks for the info - that could be the source of bugs and headaches if not properly taken into account...

Anthony, I'm glad to see you liked this blog post. Thanks!
Anthony Grace
Monday, January 26, 2009 3:56 PM
Check it out ;-)

http://codebetter.com/blogs/james.kovacs/archive/2009/01/19/visual-studio-2010-visual-studio-2008-resharper.aspx

Anthony :-)
Chris Marisic
Monday, January 26, 2009 4:30 PM
"This allows a data member to be initialized when a class is being instantiated, preventing it from being modified in any other class method – in other words, once the const data member is initialized it never changes during the class instance lifetime."

I'm not trying to nitpick but this statement is very misleading. By your argument marking the field readonly would make it IMMUTABLE which is not the case. The object can be modified unless it's a primitive type because all primitive types ARE immutable.

However the actual memory location of the object can never be modified. It'd be the equivalent of using a constant int to denote a pointer in C++. (not specifically saying you can do this just to make the comparison.)

I would recommend to further this article that you expand it to include the realm of immutability so more readers will understand what readonly brings to the table but how it falls short of immutability.
Antonio Bello
# Antonio Bello
Tuesday, January 27, 2009 1:54 AM
Good point.
If you have a C++ background, you're saying that the way how I explained could be confused with something like the C++ const modifier, which prevents a non-static method from changing the class instance state.
I'll try to make this concept clearer adding a note to the blog post. Thanks for pointing it out :)

Anthony, nice news. ReSharper is the perfect complement for Visual Studio, I hope that it won't be a scaled down version.
Chris Marisic
Tuesday, January 27, 2009 2:52 AM
Antonio

I'm glad you saw my point, I've seen many people make the assumption that READONLY == IMMUTABLE, I'm pretty sure I even assumed that when I was new to .NET. It's confusing since .NET natively offers no immutable collection types, I'd love for .NET 4.0 to have IListImmutable / IDictionaryImmutable even though I've rarely ever had the case that I've actually needed immutability.

Another idea, you might want to expand on the article is adding in the implications of static readonly and how it can be used to LazyLoading / Singleton construction.
Antonio Bello
# Antonio Bello
Friday, January 30, 2009 4:40 AM
Chris,

I've added a description about immutability and how the readonly modifier affects value and reference types. I hope it clarifies the concept.

As for your other suggestions, I'll try to take a look later.

btw, thanks for your ideas :)

Post Comment

Name (required)

Email (required)

Website

Enter the code shown above in the box below

 

stock icons

 

 

© 2006-2008 Developer's Corner - Powered by Elapsus   |  Privacy Statement  |  Terms Of Use