Suppose you could do something like this:
// NOT REAL CODE
public interface IMyInterface { }
public class MyRealClass : IMyInterface { }
...
public class Domain
{
private List<MyRealClass> myList;
public IList<IMyInterface> Interfaces { get { return (IList<IMyInterface>)this.myList; } }
}
What's to stop a user of this Domain
class from doing this?
public class MyFakeClass : IMyInterface { }
...
domain.Interfaces.Add(new MyFakeClass());
Obviously this would be problematic because myList
is in reality a List<MyRealClass>
, but the compiler cannot ensure that only instances of MyRealClass
are added to the list (it would require a run-time check). In other words, this kind of behavior is not type-safe, so the compiler won't allow it.
You can expose a IList
/ ICollection
of IMyInterface
—which is type-safe, but doesn't ensure that only your MyRealClass
is added to the list.
public class Domain
{
private List<IMyInterface> myList;
public IList<IMyInterface> Interfaces { get { return this.myList; } }
}
Alternatively, you can expose an IEnumerable
of IMyInterface
(or IReadOnlyList
, as zmbq suggests)—since the type parameter is covariant (see Covariance and Contravariance in Generics). Although this would not allow you add new items to the collection.
public class Domain
{
private List<MyRealClass> myList;
public IEnumerable<IMyInterface> Interfaces { get { return this.myList; } }
}
Another solution would be to implement your own collection class that actually implements of IList<IMyInterface>
but throws an exception if a user tries to insert anything other than a MyRealClass
. This isn't a particularly elegant solution and in practical terms, it's not any different from simply exposing an IList<MyRealClass>
.