Click here to Skip to main content
Licence CPOL
First Posted 6 Mar 2008
Views 9,766
Bookmarked 7 times

Emulate Const Correctness in C#

By | 6 Mar 2008 | Article
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.

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?

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:

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:

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:

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:

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.

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)

About the Author

Glenn Dawson

Software Developer

United States United States

Member

Austin, TX

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralActually, I did not need this feature on C#... PinmemberSergei Arhipenko4:31 11 Mar '08  
GeneralRe: Actually, I did not need this feature on C#... PinmemberGlenn Dawson10:19 11 Mar '08  
The value of the A property of myClass can still be modified using that method.
 
I'm attempting to emulate the following C++ call: void MyFunction(const MyClass& myClass) const
 
        static void Main(string[] args)
        {
            MyClass c = new MyClass(0);
            IMyClassReadOnly r = c;
 
            // c.A is 0 before function call
            MyFunction(new ReadOnlyParameter<MyClass>(c));
 
            // c.A is now 1
            Console.WriteLine(c.A);
 
            MyFunction2(c);
            MyFunction2(r);
 
            MyFunction3(c);
            MyFunction3(r); // fails to compile, MyFunction3 could modify r
        }
 
        public static void MyFunction(ReadOnlyParameter<MyClass> myClass)
        {
            myClass.Value.A = 1; // this is allowed to be set
        }
 
        public static void MyFunction2(IMyClassReadOnly myClass)
        {
            myClass.A = 1; // fails to compile, function is not allowed to modify a parameter
                // passed as read only
        }
 
        public static void MyFunction3(MyClass myClass)
        {
            myClass.A = 1;
        }

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

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120529.1 | Last Updated 7 Mar 2008
Article Copyright 2008 by Glenn Dawson
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid