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.

5
  • 3
    To me, your "workaround hack" feels like the right way to do it... Commented Apr 7, 2011 at 21:26
  • 2
    Do you even have to cast _menuCollection to IList<IMenuItem>? If ObservableCollection<MenuItemBase> satisfies IList<IMenuItem>, you should just be able to return it directly... Shouldn't you? Commented Apr 7, 2011 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. Commented Apr 7, 2011 at 21:33
  • BTW, your as will always return null... Commented Apr 7, 2011 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 Commented Apr 8, 2011 at 2:37

3 Answers 3

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#.

Sign up to request clarification or add additional context in comments.

2 Comments

These tricks you mention the C++/CLI does might make an interesting read ;)
@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.
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.

2 Comments

-1 He doesn't defines a setter in his MenuCollection property of his interface, only a getter.
@Stormenet: Obviously, but the rules of the language cannot change based on whether you define a setter or not.
0

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

See this question: Question about C# covariance

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.