Search code examples
c#constants

Read-only ("const"-like) function parameters of C#


Coming from a C++ background, I'm used to sticking the const keyword into function definitions to make objects being passed in read-only values. However, I've found out that this is not possible in C# (please correct me if I'm wrong). After some Googling, I arrived at the conclusion that the only way to make a read-only object is to write an interface that only has 'get' properties and pass that in instead. Elegant, I must say.

public interface IFoo
{
  IMyValInterface MyVal{ get; }
}

public class Foo : IFoo
{
  private ConcreteMyVal _myVal;

  public IMyValInterface MyVal
  {
    get { return _myVal; }
  }
}

I would pass it into:

public void SomeFunction(IFoo fooVar)
{
  // Cannot modify fooVar, Excellent!!
}

This is fine. However, in the rest of my code, I would like to modify my object normally. Adding a 'set' property to the interface would break my read-only restriction. I can add a 'set' property to Foo (and not IFoo), but the signature expects an interface rather than a concrete object. I would have to do some casting.

// Add this to class Foo. Might assign null if cast fails??
set { _myVal = value as ConcreteMyVal; }

// Somewhere else in the code...
IFoo myFoo = new Foo;
(myFoo as Foo).MyFoo = new ConcreteMyVal();

Is there a more elegant way of replicating const or making read-only function parameters without adding another property or a function?


Solution

  • I think you may be looking for a solution involving two interfaces in which one inherits from the other:

    public interface IReadableFoo
    {
        IMyValInterface MyVal { get; }
    }
    
    public interface IWritableFoo : IReadableFoo
    {
        IMyValInterface MyVal { set; }
    }
    
    public class Foo : IWritableFoo 
    {
        private ConcreteMyVal _myVal;
    
        public IMyValInterface MyVal
        {
            get { return _myVal; }
            set { _myVal = value as ConcreteMyVal; }
        }
    }
    

    Then you can declare methods whose parameter type “tells” whether it plans on changing the variable or not:

    public void SomeFunction(IReadableFoo fooVar)
    {
        // Cannot modify fooVar, excellent!
    }
    
    public void SomeOtherFunction(IWritableFoo fooVar)
    {
        // Can modify fooVar, take care!
    }
    

    This mimics compile-time checks similar to constness in C++. As Eric Lippert correctly pointed out, this is not the same as immutability. But as a C++ programmer I think you know that.

    By the way, you can achieve slightly better compile-time checking if you declare the type of the property in the class as ConcreteMyVal and implement the interface properties separately:

    public class Foo : IWritableFoo 
    {
        private ConcreteMyVal _myVal;
    
        public ConcreteMyVal MyVal
        {
            get { return _myVal; }
            set { _myVal = value; }
        }
    
        public IMyValInterface IReadableFoo.MyVal { get { return MyVal; } }
        public IMyValInterface IWritableFoo.MyVal
        {
            // (or use “(ConcreteMyVal)value” if you want it to throw
            set { MyVal = value as ConcreteMyVal; }
        }
    }
    

    This way, the setter can only throw when accessed through the interface, but not when accessed through the class.