Reputation: 329
In C#, you are not allowed to implement an interface like this:
public interface ITest
{
IEnumerable<int> Numbers { get; set; }
}
public class CTest : ITest
{
public List<int> Numbers { get; set; }
}
My question is: Is there some software philosophy that makes this type of implementation wrong? Is it a contested paradigm, sort of like multiple inheritance, where arguments have been taking place for decades?
I can't see any reason why such an interface implementation wouldn't work just fine. If you know about ITest
, then you only get to see the IEnumerable<int>
part of CTest.Numbers
. If you know about CTest
, then you can use the entire List<int>
implementation. Since List<int>
derives from IEnumerable<int>
, why wouldn't it satisfy the Interface requirements?
Upvotes: 2
Views: 89
Reputation: 144206
The ability of subclasses to return a subtype when overriding base methods is called return type covariance
In this case your example is unsafe due to the setter for the Numbers
property:
ITest t = new CTest();
t.Numbers = new HashSet<int>();
If there were only a getter defined in ITest
then it would be safe, although C# still does not allow it - Java does however:
public interface Test {
Iterable<Integer> getNumbers();
}
public class CTest implements Test {
public List<Integer> getNumbers() {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
return numbers;
}
}
Upvotes: 9
Reputation: 152634
An interface is a contract. It tells clients exactly what methods/properties will be available and exactly what types the inputs and outputs are.
When you tell a client that any class that implements ITest
will accept any IEnumerable<int>
for the value of Numbers
, but the CTest
class will only accept a List<int>
, you have broken that contract.
If you want to implement the interface but also expose a more specific collection type you can explicitly implement the interface, but then you have to decide what to do is a client tries to "set" an IEnumerable<int>
that is not a List<int>
one option is just to create a List<int>
behind-the-scenes:
public class CTest : ITest
{
public List<int> Numbers { get; set; }
// satisfies the interface
IEnumerable<int> ITest.Numbers {
get { return Numbers; }
set { Numbers = new List<int>(value); }
}
}
Now clients that are aware of CTest
can use the List<int>
property, but clients that only know of the interface can still set the collection without casting.
Upvotes: 3