8

Is it possible to define an Interface with optional implementation methods? For example I have the following interface definition as IDataReader in my core library:

public interface IDataReader<T> {
  void StartRead(T data);
  void Stop();
}

However, in my current implementations, the Stop() method has never been used or implemented. In all my implementation classes, this method has to be implemented with throw NotImplementedExcetion() as default:

class MyDataReader : IDataReader<MyData> {
   ...
   public void Stop()
   {
     // this none implementaion looks like uncompleted codes
     throw NotImplementedException();
   }

Of course, I can remove the throw exception code and leave it empty.

When I designed this data reader interface, I thought it should provide a way to stop the reading process. Maybe we will use Stop() sometime in the future.

Anyway, not sure if it is possible to make this Stop() method as an optional implementation method? The only way I can think is to either to define two interfaces one with stop and another without such as IDataReader and IDataReader2. Another option is to break this one into to interfaces like this:

 interface IDataReader<T> {
    void StartRead(T data);
 }

 interface IStop {
    void Stop();
 }

In my implementation cases, I have to cast or use as IStop to check if my implementation supports Stop() method:

 reader.StartRead(myData);
 ....
 // some where when I need to stop reader
 IStop stoppable = reader as IStop;
 if (stoppable != null ) stoppable.Stop();
 ...

Still I have to write those codes. Any suggestions? Not sure if there is any way to define optional implementation methods in an interface in .Net or C#?

David.Chu.ca
  • 33,436
  • 60
  • 141
  • 188

6 Answers6

7

Interesting. I'll have to quote you here:

However, in my current implementations, the Stop() method has never been used or implemented. In all my implementation classes, this method has to be implemented with throw NotImplementedExcetion() as default:

If this is the case, then you have two options:

  1. Remove the Stop() method from the interface. If it isn't used by every implementor of the interface, it clearly does not belong there.
    • Instead of an interface, convert your interface to an abstract base class. This way there is no need to override an empty Stop() method until you need to.

Update The only way I think methods can be made optional is to assign a method to a variable (of a delegate type similar to the method's signature) and then evaluating if the method is null before attempting to call it anywhere.

This is usually done for event handlers, wherein the handler may or may not be present, and can be considered optional.

Jon Limjap
  • 89,838
  • 14
  • 96
  • 150
  • I see your point. I have actually removed the method. This is in my control. However, after I done that, I tried to find out if there is any way to make method options in .Net? I really like to keep it there as optional in case I have to stop the process through interface. – David.Chu.ca Jul 24 '09 at 05:30
6

For info, another approach fairly common in the BCL is Supports* on the same interface, i.e.

bool SupportsStop {get;}
void Stop();

(examples of this, for example, in IBindingList).

I'm not pretending that it is "pure" or anything, but it works - but it means you now have two methods to implement per feature, not one. Separate interfaces (IStoppableReader, for example) may be preferable.

For info, if the implementation is common between all implementations, then you can use extension methods; for a trivial example:

public static void AddRange<T>(this IList<T> list, IEnumerable<T> items) {
    foreach(T item in items) list.Add(item);
}

(or the equivalent for your interface). If you provide a more specialized version against the concrete type, then it will take precedence (but only if the caller knows about the variable as the concrete type, not the interface). So with the above, anyone knowingly using a List<T> still uses List<T>'s version of AddRange; but if the have a List<T> but only know about it as IList<T>, it'll use the extension method.

Marc Gravell
  • 927,783
  • 236
  • 2,422
  • 2,784
5

If the method is inappropriate for your implementation, throw InvalidOperationException just like most iterators do when you call Reset on them. An alternative is NotSupportedException which tends to be used by System.IO. The latter is more logical (as it has nothing to do with the current state of the object, just its concrete type) but the former is more commonly used in my experience.

However, it's best to only put things into an interface when you actually need them - if you're still in a position where you can remove Stop, I would do so if I were you.

There's no unified support for optional interface members in the language or the CLR.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
4

If no classes in your code actually implement Stop(), and you don't have definite plans to do so in the future, then you don't need it in your interface. Otherwise, if some but not all of your objects are "stoppable", then the correct approach is indeed to make it a separate interface such as IStoppable, and the clients should then query for it as needed.

Pavel Minaev
  • 94,882
  • 25
  • 209
  • 280
0

If your implementation does not implement the interface method Stop, then it breaks obviousily the contract that comes with your interface. Either you implement the Stop method appropriately (not by throwing an Exception and not by leaving it empty) or you need to redesign your interface (so to change the contract).

Best Regards

Oliver Hanappi
  • 11,202
  • 7
  • 50
  • 67
0

C# version 4 (or vNext) is considering default implementation for interfaces - I heard that on channel9 a few months ago ;).

Interfaces with default implementation would behave somewhat like abstract base classes. Now that you can inherit multiple interfaces this could mean that C# might get multiple inheritance in form of interfaces with default implementations.

Until then you might get away with extension methods...

Or your type could make use of the delegates.

interface IOptionalStop
{
    Action Stop { get; }
}

public class WithStop : IOptionalStop
{
    #region IOptionalStop Members

    public Action Stop
    {
        get;
        private set;
    }

    #endregion

    public WithStop()
    {
        this.Stop =
            delegate
            {
                // we are going to stop, honest!
            };
    }
}

public class WithoutStop : IOptionalStop
{
    #region IOptionalStop Members

    public Action Stop
    {
        get;
        private set;
    }

    #endregion
}


public class Program
{
    public static string Text { get; set; }


    public static void Main(string[] args)
    {
        var a = new WithStop();

        a.Stop();

        var o = new WithoutStop();

        // Stop is null and we cannot actually call it
        a.Stop();

    }
}
Community
  • 1
  • 1
zproxy
  • 3,391
  • 3
  • 37
  • 43
  • 1
    There is no "default implementation for interfaces" in C# 4.0. – Pavel Minaev Jul 24 '09 at 05:29
  • Too sad. I think this should be very easy to do it. Just add a new key directive work Optional before a methods so that .net compiler will generate empty body codes for this method. Call this optional method if not not implemented will do nothing and no exception. Of course, this may open a door for people to throw in too many bad interfaces. There is a value to have optional implementation but it may be abused. Just my comment. – David.Chu.ca Jul 24 '09 at 05:55
  • I see some typos. Sorry for that. It is too late in Canada now. – David.Chu.ca Jul 24 '09 at 05:56
  • What about the delegate approach in the example i had given which would work with current version of c#? – zproxy Jul 24 '09 at 05:58
  • I like it. I thought about delegate but not as clean as yours. However, still I have check if the delegate is null or not and define another delegate to implement. – David.Chu.ca Jul 24 '09 at 06:09
  • You could create an exteinsion method .InvokeSafe() for that to ignore if target delegate is null... – zproxy Jul 24 '09 at 06:11