3

Probably a simple question.

I have an interface (MyInterface) that defines a property like so:

IList<IMenuItem> MenuCollection { get; }

And the class implementing MyInterface

public class MyClass : MyInterface
{
    public ObservableCollection<MenuItemBase> MenuCollection 
    {
       get
       {
           ...
       }
    }
    ....
}

From what I read, ObservableCollection inherits from a Collection which is a IList, and I have MenuItemBase class that imlements IMenuItem - wouldn't that satisfy the interface?

I suppose interfaces must be implemented explicitly?

I also tried this:

public class MyClass : MyInterface
{
    public IList<IMenuItem> MenuCollection MenuCollection 
    {
       get
       {
           if(_menuCollection == null)
              _menuCollection = new ObservableCollection<MenuItemBase>();
           return _menuCollection as IList<IMenuItem>;
       }
    }
    private ObservableCollection<MenuItemBase> _menuCollection;
}

Seems like a workaround hack (and I did run into a few issues saying that MenuCollection was not instantiated) to get the interface to be satisfied.... is there a better way to implement IInterface1<IInterface2> objects?

The reason why I need this kind of abstraction is because I'm building a prism / unity app and want to decouple the menu viewmodel as much as possible from the ribbon ui that displays the menu.

Chris Klepeis
  • 9,783
  • 16
  • 83
  • 149
  • 3
    To me, your "workaround hack" feels like the right way to do it... – Stormenet Apr 07 '11 at 21:26
  • 2
    Do you even have to cast `_menuCollection` to `IList`? If `ObservableCollection` satisfies `IList`, you should just be able to return it directly... Shouldn't you? – Dan J Apr 07 '11 at 21:31
  • 1
    Further, I agree with @Stormenet that this isn't really a "hack". You've declared a method to have a generic return type, so its implementation needs a generic return type. This means a caller of the method doesn't need to worry about the concrete internal types used by the method implementation. Looks good to me. – Dan J Apr 07 '11 at 21:33
  • BTW, your `as` will always return null... – digEmAll Apr 07 '11 at 21:47
  • @djacobson - I tried returning it directly, but it results in a compile time error, my only guess is that it doesnt like returning an ObservableCollection for an IList - I'll have to check the exact error again when I'm back at work – Chris Klepeis Apr 08 '11 at 02:37

3 Answers3

7

Jon is correct; this has nothing to do with generic variance. However, he does not mention that the feature you are wanting is called return type covariance.

That is, if Animal is the base type of Giraffe, then an interface method that returns an Animal, or a virtual method that returns an Animal, may be implemented/specialized by a method that returns a Giraffe. Since every Giraffe is an Animal, the contract is fulfilled.

C# does not support return type covariance; for that matter, neither does the CLR. Some languages support return type covariance; C++ for example comes to mind. (The C++/CLI implementation does some sneaky tricks to work around the limitations of the CLR.) The return type of an implemented interface method, the type of a property, and so on, all must match exactly in C#.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • These tricks you mention the C++/CLI does might make an interesting read ;) – Jon Apr 08 '11 at 02:50
  • @Jon: They're not that sneaky. Just what you'd expect. A bunch of helper methods get generated that are just proxies for the real overloads. – Eric Lippert Apr 08 '11 at 14:08
6

This has nothing to do with generic variance

There are multiple answers talking about generic variance. This has nothing to do with the example. If you try to implement a property defined as IList with an ArrayList (no generics), you will see that it's still not possible to do so.

The correct answer

This behavior is because if you could implement MenuCollection as a property of some type implementing IList<T> (or deriving from it, if it were not specified as an interface), then this would be possible:

public interface MyInterface
{
    IList<IMenuItem> MenuCollection { get; set }
}

public class MyClass : MyInterface
{
    // WARNING: Does not count as implementing the interface -- with good reason
    public ObservableCollection<MenuItemBase> MenuCollection { get; set; }
}

var myClass = new MyClass();
var classAsInterface = (MyInterface) myClass; // This is OK of course

classAsInterface.MenuCollection = new List<MenuItemBase>(); // OOPS!!

In this last line, you have assigned a List<MenuItemBase> (which is OK as far as MyInterface can tell, since MyInterface.MenuCollection is of type IList<MenuItemBase>) to a property that is of type ObservableCollection<MenuItemBase>.

Of course this is not legal to do, as List<MenuItemBase> clearly is not derived from ObservableCollection<MenuItemBase>. But it would be a possibility if you could implement the interface like that.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • -1 He doesn't defines a setter in his `MenuCollection` property of his interface, only a getter. – Stormenet Apr 08 '11 at 07:05
  • 4
    @Stormenet: Obviously, but the rules of the language cannot change based on whether you define a setter or not. – Jon Apr 08 '11 at 11:54
0

List<T> and IList<T> do not support covariance.

See this question: Question about C# covariance

Community
  • 1
  • 1
Aliostad
  • 80,612
  • 21
  • 160
  • 208