Readonly Method Level Variables in C# .NET

 

Readonly method level variables are useful if you want to avoid accidental assignments within the method's body, as well as to clearly express the intention of the method's code. C# is missing such built-in capability, so I'm introducing it here.

 

Why Do We Need Readonly Variables Inside Methods?

C# supports readonly fields, readonly properties (by keeping non-public setter accessor, or omitting it in C# 6), constant fields, and even method level constants (constants defined within method). After seeing so many things in the language that follow this rigor way of expressing our intentions, it would be natural to expect method level readonly variables as well.

When I asked a Microsoft representative about such feature, I think he was too much excited about the new C# 6 capabilities and didn't see a big need to have method level readonly variables. Well, maybe you agree with him and then no problem, you don't have to use this approach. But I feel it's necessary for the code's maintainability. Same way as we can attach "const" keyword to the method level variable, why can't we attach "readonly" keyword to them? Constants can only have values which can be compiled into metadata, so the need for readonly still exists (same way as class level constant fields cannot hold everything that readonly fields can).

If you agree that this is a missing capability, then this new solution is for you.

 

Let Me Introduce You To: Readonly Method Variables

Let's start from sample - this is what you can achieve if you have this capability in your code:

using System;

namespace DotNetExtensions.Samples
{
    public sealed class ReadOnlySample : ISample
    {
        public void Run()
        {
            var readOnlyInt = 10.AsReadOnly();
            Console.WriteLine($"Just created readonly int with value {readOnlyInt}."); // 10

            Console.WriteLine($"{nameof(readOnlyInt)} == 10 ? {readOnlyInt == 10}."); // true

            Console.WriteLine("Now using the readonly int inside loop as a number.");
            for (int i = 0; i < readOnlyInt; i++)
            {
                Console.WriteLine($"Counting --- {i}..."); // 0,1,...,10
            }
        }
    }
}

 

Few interesting points:

  • You cannot assign another value to readOnlyInt variable (otherwise, you'll have compile time error). This ensures that you won't accidentally overwrite a value which you need to keep unchanged throughout the method execution.
  • You can mark any existing value (either primitive or non primitive) as readonly, by invoking AsReadOnly() extension method on it.
  • Readonly value (which is actually ReadOnly<T> type) can be used in place of the actual wrapped value (anywhere you would use T). This is possible due to implicit conversion operator that comes with ReadOnly<T> structure.

 

How to Implement

If you simply want to use this implementation in your project as is, you can find it in my GitHub repository: https://github.com/ttutisani/DotNetExtensions. It's open source and I permit usage of it in any kind of project, including but not limited to personal, commercial, non-profit, open source, and any other.

If you simply want to take the code and make it your own implementation, that is also fine. You can take code from GitHub, or from below:

namespace DotNetExtensions
{
    public struct ReadOnly<T>
    {
        public ReadOnly(T value)
        {
            Value = value;
        }

        public T Value { get; }

        public static implicit operator T(ReadOnly<T> readOnly)
        {
            return readOnly.Value;
        }

        public override string ToString()
        {
            return Value?.ToString();
        }
    }

    public static class ReadOnly
    {
        public static ReadOnly<T> Value<T>(T value)
        {
            return new ReadOnly<T>(value);
        }

        public static ReadOnly<T> AsReadOnly<T>(this T value)
        {
            return new ReadOnly<T>(value);
        }
    }
}

 

Further Thinking About Missing Features in C# .NET

You think there are other nice features missing in C#? I also think so. That's why I'm slowly working on finding them and putting them together under my GitHub repository: https://github.com/ttutisani/DotNetExtensions - which is supposed to eventually gather "Little things that are missing from .NET".

 

Share If You Like It