On Apr 7, 2008, at 09:28, Yossi Kreinin wrote:
I would argue that more people would intuitively think that
IList<B> inherits from IList<A> if B inherits from A than that it
does not (and that generic methods are instead required).
I think that the only reasonable test for "is Y a subclass of X?"
is "can I substitute Y objects whenever X objects are used?".
Y="collections of derived class objects" fails this test when
X="collections of base class objects".
I agree with the test of "can I substitute Y objects whenever X
objects are used?", but I don't follow -- in your example -- under
which circumstances I would be unable to substitute IList<B> wherever
IList<A> is expected.
(All examples are in untested C#.)
If covariance is supported, then I can do the following:
class A
{
property bool IsAvailable { get; set; }
property string Name { get; }
void Save(Stream stream);
}
class B : A
{
property int NumAttempts { set; }
void UpdateFromWeb();
}
void PlayWithAList(IList<A> list)
{
Stream stream = new MemoryStream();
foreach (A a in list)
{
if (a.Name == "test1")
{
a.IsAvailable = !a.IsAvailable;
}
a.Save();
}
}
void main()
{
IList<B> bList = GetListFromWebService();
PlayWithAList(bList);
}
Where can I not substitute A with B in the method "PlayWithAList"?
Without covariance, I can write the method like this:
void PlayWithAList<T>(IList<T> list) where T : A
{
// body is the same
}
As mentioned in the previous mail, this works just fine *as long as I
have control of the method definition*. If the method definition is
in a framework and the designer didn't make the method generic, then
you're out of luck and have to either convert or cast the argument,
which is a shame.
Granted, with containers, you leave yourself open to the possibility
of adding an element to the list which will cause a runtime error.
Suppose we also have class C:
class C : A { }
void PlayWithAList(IList<A> list)
{
list.Add(new C());
}
If I'd passed in IList<B> as the parameter to this method, it would
cause a runtime exception even though the compiler had evaluated
statically that it was legal.
Interestingly, C# does support covariance in one limited case: when
calling delegates.
Assume the following delegate definition:
delegate void PlayWithA(A a);
Then let's redefine the method call "PlayWithAList" to be:
void PlayWithAList(IList<A> list, PlayWithA func)
{
Stream stream = new MemoryStream();
foreach (A a in list)
{
if (a.Name == "test1")
{
a.IsAvailable = !a.IsAvailable;
}
func(a);
a.Save();
}
}
Since C# supports covariance, you can call "PlayWithAList" like this:
void main()
{
IList<B> bList = GetListFromWebService();
PlayWithAList(
bList,
delegate(B b)
{
b.UpdateFromWeb(); // Method is only defined in B, but
covariance works here!
}
);
}
Cheers
Marco
--
Marco Von Ballmoos
http://earthli.com - Home of the earthli WebCore; PHP web sites made
simple.