|
ms
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Equals() and inheritanceA straightforward translation to C# causes a stack overflow. Why does b.Equals(ba) in the snippet below not understand that it should call (ba as B).Equals(b) inside? Thanks in advance, Alex code sample follows: class A { protected int a = 1; public A() {} public override bool Equals(object obj) { return obj.Equals(this); } public virtual bool Equals(A obj) { return obj.a == this.a; } } class B : A { private static int cnt = 0; protected int b = 2; public B() { a = cnt++; } public override bool Equals(A obj) { return obj.Equals(this); } public virtual bool Equals(B obj) { return obj.b == this.b; } } static void Main(string[] args) { A a = new A(); A a1 = new A(); B b = new B(); B b1 = new B(); A ba = new B(); Console.WriteLine(a.Equals(a1)); // OK, prints true Console.WriteLine(a.Equals(b)); // OK, prints false Console.WriteLine(b.Equals(a)); // OK, prints false Console.WriteLine(b.Equals(b1)); // OK, prints true Console.WriteLine(b.Equals(ba)); // stack overflow Console.WriteLine(ba.Equals(b)); // stack overflow } PS I know a workaround for this problem, but it looks ugly to me: B.Equals should be written as: public override bool Equals(A obj) { B Bobj = obj as B; if (Bobj != null) return Bobj.Equals(this); else return obj.Equals(this); } This solution does not satisfy me becasue it is equivalent to writing the case of obj being of class B explicitly, like this: public override bool Equals(A obj) { B Bobj = obj as B; if (Bobj != null) return this.b == Bobj.b; else return obj.Equals(this); } On Nov 26, 11:57 am, Alex Cohn <AlexC***@discussions.microsoft.com>
wrote: > In C++, there is an easy technique to provide an overloaded Equals() method. Overloading is done at compile time, so for> A straightforward translation to C# causes a stack overflow. Why does > b.Equals(ba) in the snippet below not understand that it should call (ba as > B).Equals(b) inside? b.Equals(ba) the compiler only knows that b is of type B, and ba is of type A. It therefore calls B.Equals(A). Now let's look at the code for B.Equals(A). The compiler only knows about obj as A, and so the line obj.Equals(this) will call A.Equals(A) (there's no overload for A.Equals(B)). You've overridden that in B.Equals(A), hence the recursion. At what point would you expect it to behave differently? Jon In C++, inheritance works through a virtual table. Each object refers to a
table of virtual functions. The overloaded Equals() for object of type B is in the object's table. Therefore, even if the object is "known" to the compiler as type A reference, the call will peform the B::Equals() call. I am puzzled if C# resolves all overloads at compile time. Actually, it does not. In the following example, the compiler can not know which method to call, but the output is correct: class A { public override string ToString() { return "A"; } } class B: A { public override string ToString() { return "B"; } } static void Main(string[] args) { A qq = null; if (args.length == 1) qq = new A(); else qq = new B(); Console.WriteLine(qq.ToString()); } Alex Show quote "Jon Skeet [C# MVP]" wrote: > On Nov 26, 11:57 am, Alex Cohn <AlexC***@discussions.microsoft.com> > wrote: > > In C++, there is an easy technique to provide an overloaded Equals() method. > > A straightforward translation to C# causes a stack overflow. Why does > > b.Equals(ba) in the snippet below not understand that it should call (ba as > > B).Equals(b) inside? > > Overloading is done at compile time, so for > b.Equals(ba) > the compiler only knows that b is of type B, and ba is of type A. > > It therefore calls B.Equals(A). > > Now let's look at the code for B.Equals(A). The compiler only knows > about obj as A, and so the line > obj.Equals(this) will call A.Equals(A) (there's no overload for > A.Equals(B)). You've overridden that in B.Equals(A), hence the > recursion. > > At what point would you expect it to behave differently? > > Jon > On Nov 26, 2:16 pm, Alex Cohn <AlexC***@discussions.microsoft.com>
wrote: > In C++, inheritance works through a virtual table. Each object refers to a You're confusing overloading and overriding. Overriding is performed> table of virtual functions. The overloaded Equals() for object of type B is > in the object's table. Therefore, even if the object is "known" to the > compiler as type A reference, the call will peform the B::Equals() call. I am > puzzled if C# resolves all overloads at compile time. at execution time, but overloading is performed at compile time. I believe C++ works the same way. Jon I am sorry if I caused confusion with my usage of words "overload" and
"override". However, the compiler does not care much about the terms. I still do not understand why the expected override for ToString() is used, but the expected override for Equals() is not. On Nov 26, 2:57 pm, Alex Cohn <AlexC***@discussions.microsoft.com>
wrote: > I am sorry if I caused confusion with my usage of words "overload" and It certainly does - if you try using "overload" as a keyword instead> "override". However, the compiler does not care much about the terms. of "override" you'll find it cares an awful lot. > I still do not understand why the expected override for ToString() is used, but the Because you're expecting an *overload* of Equals rather than an> expected override for Equals() is not. *override*. When A.Equals(A) is called, that will result in the override B.Equals(A) being executed. It will *never* result (directly) in B.Equals(B) being executed, because that's a different signature. Jon Thank you for your patience. I have tried to answer your last reply with a
more descriptive example, and finally figured out what went wrong in my head. Alex Show quote "Jon Skeet [C# MVP]" wrote: > On Nov 26, 2:57 pm, Alex Cohn <AlexC***@discussions.microsoft.com> > wrote: > > I am sorry if I caused confusion with my usage of words "overload" and > > "override". However, the compiler does not care much about the terms. > > It certainly does - if you try using "overload" as a keyword instead > of "override" you'll find it cares an awful lot. > > > I still do not understand why the expected override for ToString() is used, but the > > expected override for Equals() is not. > > Because you're expecting an *overload* of Equals rather than an > *override*. > > When A.Equals(A) is called, that will result in the override > B.Equals(A) being executed. It will *never* result (directly) in > B.Equals(B) being executed, because that's a different signature. > > Jon > On Nov 26, 3:50 pm, Alex Cohn <AlexC***@discussions.microsoft.com>
wrote: > Thank you for your patience. I have tried to answer your last reply with a Goodo :) I wasn't sure how I was going to explain it any other way -> more descriptive example, and finally figured out what went wrong in my head. it's one of those problems where it's very easy to miscommunicate by referring to different methods which have the same name, because that's inherent in the situation... Jon |
|||||||||||||||||||||||