Click here to Skip to main content
15,891,372 members
Articles / Programming Languages / C#
Article

Emulate Const Correctness in C#

Rate me:
Please Sign up or sign in to vote.
3.36/5 (6 votes)
6 Mar 2008CPOL1 min read 20.1K   7   2
How to use interfaces to emulate const correctness

Introduction

In C++, one can use the const keyword to prevent a called function from modifying a parameter that is passed by reference. C# does not allow use of const in this manner. This article explains how to emulate that functionality through the use of an interface.

Using the Code

First, an interface is created that exposes only read-only properties and/or methods. This interface is then implemented by the class to be protected.

C#
public interface IMyClassReadOnly
{
    int A { get; }
}

public class MyClass : IMyClassReadOnly
{
    private int a;

    public int A
    {
        get { return a; }
        set { a = value; }
    }

    public MyClass(int a)
    {
        this.a = a;
    }
}

public class Program
{
    static void Main(string[] args)
    {
        MyClass myclass = new MyClass(0);
        IMyClassReadOnly myclassReadOnly = myclass as IMyClassReadOnly;

        DoNotModify(myclass); // succeeds
        Modify(myclass); // succeeds

        DoNotModify(myclassReadOnly); // succeeds
        //Modify(myclassReadOnly); // fails to compile,
        // cannot convert from IMyClassReadOnly to MyClass
    }

    private static void DoNotModify(IMyClassReadOnly c)
    {
        Console.WriteLine("DoNotModify: {0}", c.A);

        //c.A = 42; // fails to compile
    }

    private static void Modify(MyClass c)
    {
        c.A = 42;
    }
}

That worked as most people would expect. The modify function is prevented from writing to the value of A, with a compile time error, because it cannot convert from the interface to the class type.

What would happen if A were a reference type, as S is in the following code, instead?

C#
public class SomeOtherClass
{
    private int b;

    public int B
    {
        get { return b; }
        set { b = value; }
    }

    public SomeOtherClass(int b)
    {
        this.b = b;
    }
}

public interface IMyClassReadOnly
{
    int A { get; }
    SomeOtherClass S { get; }
}

public class MyClass : IMyClassReadOnly
{
    private int a;
    private SomeOtherClass s;

    public int A
    {
        get { return a; }
        set { a = value; }
    }

    public SomeOtherClass S
    {
        get { return s; }
        set { s = value; }
    }

    public MyClass(int a)
    {
        this.a = a;
        s = new SomeOtherClass(a);
    }
}

public class Program
{
    static void Main(string[] args)
    {
        MyClass myclass = new MyClass(0);
        IMyClassReadOnly myclassReadOnly = myclass as IMyClassReadOnly;
    
        myclass.S = new SomeOtherClass(1); // succeeds
    
        myclassReadOnly.S = new SomeOtherClass(2); // fails to compile,
        // cannot be assigned to -- it is read only

        myclassReadOnly.S.B = 3; // succeeds (what?)
    }
}

The third case is allowed because SomeOtherClass exposes get and set through a property. A simple fix right? You just need to add a read-only interface as was done for MyClass. Unfortunately, the following doesn't work:

C#
public interface ISomeOtherClassReadOnly
{
    int B { get; }
}

public class SomeOtherClass : ISomeOtherClassReadOnly
{
    // nothing changed here
}

That doesn't work because the property in MyClass returns SomeOtherClass instead of ISomeOtherClassReadOnly. This could be remedied by adding another property to the interface and class such as:

C#
public ISomeOtherClassReadOnly SReadOnly
{
    get { return s as ISomeOtherClassReadOnly; }
}

However, now usage of the object would differ when accessing the SomeOtherObject member. Instead, one can modify the IMyClassReadOnly interface to read:

C#
public interface IMyClassReadOnly
{
    int A { get; }
    ISomeOtherClassReadOnly S { get; }
}

Now the example won't compile because there is a conflict between the property S as defined in the class and the interface. How can this be resolved? Simple, implement the interface version explicitly, as follows:

C#
public class MyClass : IMyClassReadOnly
{
    // other code omitted

    ISomeOtherClassReadOnly IMyClassReadOnly.S
    {
        get { return s as ISomeOtherClassReadOnly; }
    }
}

Now, if the third case is retested, it will fail at compile time.

C#
public class Program
{
    static void Main(string[] args)
    {
        MyClass myclass = new MyClass(0);
        IMyClassReadOnly myclassReadOnly = myclass as IMyClassReadOnly;
        
        myclassReadOnly.S.B = 3; // fails at compile time,
            // cannot be assigned to -- it is read only
    }
}

Note that this method of protecting constness is not infallible. Obviously, the interface could be cast back to the class, but the same can be done in C++ with const_cast.

History

  • 6th March, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
Austin, TX

Comments and Discussions

 
GeneralActually, I did not need this feature on C#... Pin
Sergey Arhipenko11-Mar-08 4:31
Sergey Arhipenko11-Mar-08 4:31 
GeneralRe: Actually, I did not need this feature on C#... Pin
Glenn Dawson11-Mar-08 10:19
Glenn Dawson11-Mar-08 10:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.