views:

74

answers:

5

I have two interfaces like these:

public interface IMyInterface1
{
    string prop1 { get; set; }
    string prop2 { get; set; }
}

public interface IMyInterface2
{
    string prop1 { get; set; }
    IList<IMyInterface1> prop2 { get; set; }
}

I have defined two classes that implement the interfaces:

public class MyClass1 : IMyInterface1
{
     public string prop1 {get; set;}
     public string prop2 {get; set;}
}

public class MyClass2 : IMyInterface2
{
     public string prop1 {get; set;}
     public IList<MyClass1> prop2 {get; set;}
}

but when I build the code I have the following error message:

'ClassLibrary1.MyClass2' does not implement interface member 'ClassLibrary1.IMyInterface2.prop2'. 'ClassLibrary1.MyClass2.prop2' cannot implement 'ClassLibrary1.IMyInterface2.prop2' because it does not have the matching return type of 'System.Collections.Generic.IList'

How can I do to implement the "IList prop2" of IMyInterface2 on my class?

A: 

MyClass2 must implement the property as it's declared in the interface:

public class MyClass2 : IMyInterface2
{
     public string prop1 {get; set;}
     public IList<IMyInterface1> prop2 {get; set;}
}

Even though MyClass1 implements IMyInterface1, it may cause problems in cases such as:

IMyInterface2 myInterface2 = new MyClass2();
myInterface2.prop2.Add(new OtherImplementationOfIMyInterface1());

Anyone uses the class expects to be able to assign any instance which implements IMyInterface1 but the class expects concrete instances of MyClass1.

Elisha
+1  A: 

This is because your interface MyInterface2 has a property which is a generic list of type IInterface1 and in the class that implements this interface i.e. MyClass2 you have declared the property as being a list of type MyClass1.

To fix, either change the class definition of Prop2 to be a list of MyInterface1 or change the interface definition of Prop2 to be a list of MyClass1.

e.g.

public interface MyInterface2
{
    public IList<MyInterface1> Prop2 { get; set; }
}

public class MyClass2 : MyInterface2
{
    public IList<MyInterface1> Prop2 { get; set; }
}
James
+1  A: 

I'm not sure if I'd call this a duplicate of the question from yesterday but...

.NET doesn't support return type covariance. That means you can't return a derived type from a class that implements an interface that requires a more generic type.

The workaround is to explicitly implement the member of the interface giving you trouble:

public class MyClass2 : IMyInterface2
{
    public string prop1 { get; set; }
    public IList<MyClass1> prop2 { get; set; }
    public IList<IMyInterface1> IMyInterface2.prop2
    {
        get { return prop2.Cast<IMyInterface1>.ToList(); }
        set { prop2 = value.Cast<MyClass1>().ToList(); }
    }
}

In this case, though, explicitly implementing the interface like that will cause issues if you try to call IMyInterface.prop2.Add() since IMyInterface.prop2 doesn't reference the same collection as prop2 anymore.

The other way to solve the problem would be to implement Adam's suggestion and make IMyInterface2 generic so you can supply any type that implements IMyInterface1.

Justin Niessner
`Enumerable.Cast` produces an `IEnumerable<T>`, not `IList<T>`. This isn't really a suitable workaround, as you're going to have to set `prop2` equal to a new reference no matter how you go about it.
Adam Robinson
@Adam Robinson - It's definitely not perfect. And yes, if you were trying to add items to IMyInterface2.prop2 you would have issues. If the OP is using .NET 4, the call to Cast() is redundant since you could take advantage of the new covariance/contravariance features.
Justin Niessner
+1  A: 

Your interface requires that the implementing class provide a property that is of type IList<IMyInterface1>, not IList<class that implements IMyInterface1>.

You'll need to make IMyInterface2 generic if you want this to work:

public interface IMyInterface2<T> where T : IMyInterface1
{
    string prop1 { get; set; }
    IList<T> prop2 { get; set; }
}

Then MyClass2 becomes:

public class MyClass2 : IMyInterface2<MyClass1>
{
     public string prop1 {get; set;}
     public IList<MyClass1> prop2 {get; set;}
}
Adam Robinson
I was about to post the exact same code :)
Johann Blais
A: 

The issue with trying to implement the code like that is this. If the compiler allowed you to compile that code, then you could write code like this:

public class MyClass3 : IMyInterface1 
{ 
    public string prop1 {get; set;} 
    public string prop2 {get; set;} 
} 

...

MyClass2 mc2 = new MyClass2();
IMyInterface2 mi2 = mc2;
mi2.prop2 = new List<MyClass3>();

The interface should allow you to put any IList<IMyInterface2> into prop2, but MyClass2 can't handle that. That's why you can't implement a writable property as you have suggested.

BlueMonkMN