C# – How to properly extend an interface with immutable properties to offer mutability via another interface

cimmutabilityinterfacesmutable

The following code snippets are simplified to demonstrate the context!
The actual interfaces and classes are POCOs having additional properties. The types are part of library I am working on, the interfaces are public API, the classes internal API.

I have:

  1. an interface IValue

    public interface IValue
    {
        Int32 Value { get; }
    }
    
  2. 2 different IConfigurableValue interfaces (2 separate git branches) which extend IValue, see further on

    public interface IConfigurableValue : IValue
    {
        void CopyFrom(IValue source); // is always part of the interface
    }
    
  3. an implementation AutoGeneratedValue

    internal class AutoGeneratedValue : IConfigurableValue
    {
        public Int32 Value { get; set; }
    
        public void CopyFrom(IValue source)
        {
            Value = source.Value;
        }
    }
    
  4. code which needs to access the getter only (via IValue) and code which requires a IConfigurableValue (no compile-time known implementation)

    External users of the library can implement IValue and IConfigurableValue on their own. Part of my library code creates an instance of AutoGeneratedValue. If the user provides a custom IConfigurableValue implementation my AutoGeneratedValue is passed to it, the user decides if to ignore it or not. Another API part accepts an instance of IValue and does something. It only needs to read the instance properties. This part of the API can be invoked without ever requiring any IConfigurableValue instances.

My goal for the library is a) to offer methods requiring an IValue instance and b) methods to auto-generated IValue instances or populate user-provided IConfigurableValue instances which can be used for part a.

I currently have 2 branches with the following IConfigurableValue definitions:

  • IConfigurableValue will extend a separate IValueConfiguration as well as IValue

    public interface IConfigurableValue : IValue, IValueConfiguration
    {
        void CopyFrom(IValue source);
    }
    
    public interface IValueConfiguration
    {
        Int32 Value { set; }
    }
    
  • and a IConfigurableValue which defines the setter itself, no additional interface

    public interface IConfigurableValue : IValue
    {
        new Int32 Value { set; }
    
        void CopyFrom(IValue source);
    }
    

With both options I need to cast IConfigurableValue to IValue to access the getter in part b of my library.
With option no. 1 I need to cast IConfigurableValue to IValueConfiguration to access the setter.

Option no. 2 denies me the usage via a setter-only interface because any IConfigurableValue is an IValue,
with no. 1 I could use the IValueConfiguration if I need it. — This is no actual requirement of my API but I don't know the future demands for the API.

Is there another option to achieve my goal and which option should be preferred?

Best Answer

Consider just doing this:

public interface IValue
{
    Int32 Value { get; }
}

public interface IConfigurableValue : IValue
{
    void SetValue(Int32 newValue);
}

The obvious disadvantage here is that callers of IConfigurableValue have to use an unusual, Java-like syntax for the setter instead of the normal C# syntax. Nevertheless, this solution is simple and easy to understand.

You should only define CopyFrom as an interface member if its implementation may vary between different implementing classes. If all implementing classes will use the same implementation of CopyFrom, just define it as an extension method instead:

public static class ConfigurableValueExtensions
{
    public static void CopyFrom(this IConfigurableValue destination, IValue source)
    {
        destination.SetValue(source.Value);
    }
}
Related Topic