5

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?

11
  • 2
    @ckerth Yes, but I have to give you props for making this a good question though. I hope it will be well received on programmers. Commented Aug 25, 2016 at 13:21
  • @SimonForsberg That's why I consider this a best-practice question. I have 2 solutions, both working but having some drawbacks. Maybe there is a better solution I don't know of yet. I don't want opinion like "I like that better", I want to know if one or the other option should be used (or preferred) for a reason. or to shorten "best practice"
    – ckerth
    Commented Aug 26, 2016 at 9:25
  • Refresh my memory. What happens if you make IConfigurableValue : IValue { Int32 Value { get; set; } } ? Does that compile?
    – RubberDuck
    Commented Aug 26, 2016 at 10:31
  • @RubberDuck first off: CS0108 -- but yes it should compile. I don't want it it for a reason of explicit implementation for get via both interfaces. Example: public class Sample : IConfigurableValue { Int32 IConfigurableValue.Value { get; set; } Int32 IValue.Value { get; } }
    – ckerth
    Commented Aug 26, 2016 at 11:03
  • 1
    Is there a reason that IConfigurableValue needs to extend IValue? You could just have them as two different interfaces implemented by the same class Commented Aug 26, 2016 at 15:25

1 Answer 1

1

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);
    }
}
1
  • This approach never exposes the property itself as mutable, simple but effective.
    – ckerth
    Commented Aug 26, 2016 at 13:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.