|
ms
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Wrong overload resolution ?Hi guys,
Please, look at the code below and try to step into it. The compiled code calls the loosely typed method public void Method1(object o) !?!? Am I right that C# compiler does wrong overload resolution ? I've used parameters of type object and string here, just to illustrate the problem. Really I have a bit more deep inheritance graph, and the things get more interesting if the strongly typed overload is like override public void Method1(BaseType x). When I call it with parameter of type SubType (that inherits BaseType) the right method is called. Thanks for any useful points. Regadrs, Vladimir Granitsky using System;using System.Diagnostics;namespace OverloadResolution{ public class Class1 { virtual public void Method1(string s) { Trace.WriteLine("Class1.Method1"); } } public class Class2 : Class1 { override public void Method1(string s) { Trace.WriteLine("Class2.Method1a"); } public void Method1(object o) { Trace.WriteLine("Class2.Method1b"); } } class Client { [STAThread] static void Main(string[] args) { string s = "blah"; Class2 o2 = new Class2(); o2.Method1(s); } }} Hi Vladimir,
I work with .NET Framework 1.0 has the same "wrong" result! Only change in "Main": static void Main(string[] args) { string s = "blah"; Class1 o2 = new Class2(); o2.Method1(s); } or adding method into Class1, and "override" it in Class2 public virtual void Method1(object o) { Console.WriteLine("Class1.Method1(object)"); } gives me "right" result... It looks like an overriden method has less "priority" than a non-overriden methods in case of overload. When i replaced method of Class2: new public void Method1(string s) { Trace.WriteLine("Class2.Method1(string)"); } then it was used by overload... Strange and creepy :-( Marcin Show quote > Hi guys, > > Please, look at the code below and try to step into it. The compiled > code calls the loosely typed method public void Method1(object o) !?!? > > Am I right that C# compiler does wrong overload resolution ? > > I've used parameters of type object and string here, just to illustrate > the problem. Really I have a bit more deep inheritance graph, and the > things get more interesting if the strongly typed overload is > like override public void Method1(BaseType x). When I call it with > parameter of type SubType (that inherits BaseType) the right method is > called. > > Thanks for any useful points. > > Regadrs, > Vladimir Granitsky > > using System; > using System.Diagnostics; > namespace OverloadResolution > { > public class Class1 > { > virtual public void Method1(string s) > { > Trace.WriteLine("Class1.Method1"); > } > } > > public class Class2 : Class1 > { > override public void Method1(string s) > { > Trace.WriteLine("Class2.Method1a"); > } > > public void Method1(object o) > { > Trace.WriteLine("Class2.Method1b"); > } > } > > class Client > { > [STAThread] > static void Main(string[] args) > { > string s = "blah"; > Class2 o2 = new Class2(); > o2.Method1(s); > } > } > } > > Hi Marcin ,
Thanks for the reply. Changes in Main() cast the o2 to Class1 and it is normal to get right result. In fact currently I resolve the problem rigth this way. adding method into Class1, and "override" it in Class2 also is interesting. But what if I do not need Method1(object o) in Class 1 ? As I getting more responses I think that the behaviour is regarding the spec, but the spec is wrongly witten here. Regards, Vladimir Show quote "Marcin Grzebski" <mgrzebski@taxussi.no.com.spam.pl> wrote in message news:d3gk0q$i39$1@nemesis.news.tpi.pl... > Hi Vladimir, > > I work with .NET Framework 1.0 has the same "wrong" result! > > Only change in "Main": > static void Main(string[] args) > { > string s = "blah"; > Class1 o2 = new Class2(); > o2.Method1(s); > } > > or adding method into Class1, and "override" it in Class2 > > public virtual void Method1(object o) { > Console.WriteLine("Class1.Method1(object)"); > } > > gives me "right" result... > > It looks like an overriden method has less "priority" than > a non-overriden methods in case of overload. > When i replaced method of Class2: > new public void Method1(string s) > { > Trace.WriteLine("Class2.Method1(string)"); > } > then it was used by overload... > > Strange and creepy :-( > > Marcin > > > Hi guys, > > > > Please, look at the code below and try to step into it. The compiled > > code calls the loosely typed method public void Method1(object o) !?!? > > > > Am I right that C# compiler does wrong overload resolution ? > > > > I've used parameters of type object and string here, just to illustrate > > the problem. Really I have a bit more deep inheritance graph, and the > > things get more interesting if the strongly typed overload is > > like override public void Method1(BaseType x). When I call it with > > parameter of type SubType (that inherits BaseType) the right method is > > called. > > > > Thanks for any useful points. > > > > Regadrs, > > Vladimir Granitsky > > > > using System; > > using System.Diagnostics; > > namespace OverloadResolution > > { > > public class Class1 > > { > > virtual public void Method1(string s) > > { > > Trace.WriteLine("Class1.Method1"); > > } > > } > > > > public class Class2 : Class1 > > { > > override public void Method1(string s) > > { > > Trace.WriteLine("Class2.Method1a"); > > } > > > > public void Method1(object o) > > { > > Trace.WriteLine("Class2.Method1b"); > > } > > } > > > > class Client > > { > > [STAThread] > > static void Main(string[] args) > > { > > string s = "blah"; > > Class2 o2 = new Class2(); > > o2.Method1(s); > > } > > } > > } > > > > > Am I right that C# compiler does wrong overload resolution ? No.If you introduce a new definition of an overloaded method then there is a specific thing to do I cannot remember of right now but it is documented. Well, I'm gonna guess that it's working according to the C# spec, but
I'm leaning toward it being a rather bad design choice on the spec writers. To follow what going on, add the lines: Class1 o1 = o2; o1.Method1(s); to the end of your Main() function. Run it, and you'll note that while o2.Method1() give the wrong response, o1.Method1() is correct. Now, replace the "override" with "new" (or just delete it). Now, o2.Method1() is correct, while o1.Method1() is wrong. So, what I THOUGHT was happening was that Method1(object) was hiding the Class1.Method1() (including hiding it's override). BUT, now change Method1(object o) to Method1(int o). Now, both o2.Method1() & o1.Method1() are correct. So, it's only hiding it if the parameters are similar (C++ would hide it based on just the name) "Vladimir Granitsky" <vl_granit***@hotmail.com> wrote in message Please, look at the code below and try to step into it. The compiled codenews:##2N1G1PFHA.2788@TK2MSFTNGP09.phx.gbl... Hi guys, calls the loosely typed method public void Method1(object o) !?!? Am I right that C# compiler does wrong overload resolution ? I've used parameters of type object and string here, just to illustrate the problem. Really I have a bit more deep inheritance graph, and the things get more interesting if the strongly typed overload is like override public void Method1(BaseType x). When I call it with parameter of type SubType (that inherits BaseType) the right method is called. Thanks for any useful points. Regadrs, Vladimir Granitsky using System;using System.Diagnostics;namespace OverloadResolution{ public class Class1 { virtual public void Method1(string s) { Trace.WriteLine("Class1.Method1"); } } public class Class2 : Class1 { override public void Method1(string s) { Trace.WriteLine("Class2.Method1a"); } public void Method1(object o) { Trace.WriteLine("Class2.Method1b"); } } class Client { [STAThread] static void Main(string[] args) { string s = "blah"; Class2 o2 = new Class2(); o2.Method1(s); } }} Hi James,
Thanks for the interesting response. I agree that the question is - Is this a C# compiler bug or a specification design issue. Will look forward for someone to answer. My comment are below. "James Curran" <jamescur***@mvps.org> wrote in message news:uU3DCO3PFHA.3668@TK2MSFTNGP14.phx.gbl... Yes, I know, This is bacause if the method is declared vurtual in the base class, the latest override will be invoked, even if the object is cast to the base type. Currently I resolve this issue by calling ((Class1)o2).Method1(s);> Well, I'm gonna guess that it's working according to the C# spec, but > I'm leaning toward it being a rather bad design choice on the spec writers. > > To follow what going on, add the lines: > Class1 o1 = o2; > o1.Method1(s); > > to the end of your Main() function. Run it, and you'll note that while > o2.Method1() give the wrong response, o1.Method1() is correct. > Now, replace the "override" with "new" (or just delete it). Now, I think we can't say wrong here. The "new" keyword prevents Class2.Method1(string s) from being an override of Class1.Method1(string s) and the rule i mentioned above do not apply. So if you have a variable of type Class1 pointing to an instance of Class2, the method of Class1 will be invoked. I think, this is normal behaviour.> o2.Method1() is correct, while o1.Method1() is wrong. > I think this is because string cannot cast to int and the compiler takes the right way.> So, what I THOUGHT was happening was that Method1(object) was hiding the > Class1.Method1() (including hiding it's override). > BUT, now change Method1(object o) to Method1(int o). Now, both o2.Method1() > & o1.Method1() are correct. So, it's only hiding it if the parameters are > similar (C++ would hide it based on just the name) Show quote > > > "Vladimir Granitsky" <vl_granit***@hotmail.com> wrote in message > news:##2N1G1PFHA.2788@TK2MSFTNGP09.phx.gbl... > Hi guys, > > Please, look at the code below and try to step into it. The compiled code > calls the loosely typed method public void Method1(object o) !?!? > > Am I right that C# compiler does wrong overload resolution ? > > I've used parameters of type object and string here, just to illustrate the > problem. Really I have a bit more deep inheritance graph, and the things get > more interesting if the strongly typed overload is like override public void > Method1(BaseType x). When I call it with parameter of type SubType (that > inherits BaseType) the right method is called. > > Thanks for any useful points. > > Regadrs, > Vladimir Granitsky > using System;using System.Diagnostics;namespace OverloadResolution{ > public class Class1 { virtual public void Method1(string s) > { Trace.WriteLine("Class1.Method1"); } } public > class Class2 : Class1 { override public void Method1(string s) > { Trace.WriteLine("Class2.Method1a"); } public void > Method1(object o) { > Trace.WriteLine("Class2.Method1b"); } } class Client > { [STAThread] static void Main(string[] args) > { string s = "blah"; Class2 o2 = new Class2(); > o2.Method1(s); } }} > > > Am I right that C# compiler does wrong overload resolution ? The compiler matches the C# specification here, even though it is surprising behavior. The full story of overload resolution is incredibly complicated and it is hard to reverse engineer the rules by looking at examples. There are two rules that apply here. 1. Overrides are not included in the set of candidate methods during resolution. This may seem counterintuitive since an override is more specific and therefore a better match. However, an override is logically the same method as the original declared in the base class. The original is still a candidate after we remove the overrides. If the remaining steps in the resolution process select the original method (i.e. the virtual version), dynamic binding will ensure that the correct override version executes. 2. All candidate methods must come from the same type. We use the lowest type in the hierarchy that contains an applicable method. We would prefer to select a method from the lowest type since it is the most specific and we assume it would perform the most appropriate operation. Only if the lowest type does not contain an applicable method do we look further up the hierarchy. We keep all the methods declared in that type and discard all methods declared in all other types. We discard methods in the other types even if their parameter list is a better match.
Show quote
> 2. All candidate methods must come from the same type. We use the I find rule #2 jarring... it runs contrary to my understanding of howlowest > type in the hierarchy that contains an applicable method. We would prefer to > select a method from the lowest type since it is the most specific and we > assume it would perform the most appropriate operation. Only if the lowest > type does not contain an applicable method do we look further up the > hierarchy. We keep all the methods declared in that type and discard all > methods declared in all other types. We discard methods in the other types > even if their parameter list is a better match. inheritance should work, let alone overloading. I had always thought that if I were to declare: public class Class1 { public void Method1(string s) {...} } public class Class2 : Class1 { public void Method1(object o) {..} } and called Class2 c2 = new Class2(); c2.Method1("Hello world"); then of course the method invoked would be the method from the base class, Class1. You're telling me that the Class2 method would be invoked instead? That's loopy! No, that makes sense (It's also how it done in C++). Consider if you
had written this without Class1.Method1. Naturally, you'd expect Class2.Method1 to be called, as it's the only Method1. Now consider if, years later, some maintence programmer added Method1 to Class1. Next time you recompile your code, suddenly a different function is being called. That's not the way it should work. So, method1 in a derived class hide base methods with the same name. Show quote "Bruce Wood" <brucew***@canada.com> wrote in message news:1113324434.303178.152200@g14g2000cwa.googlegroups.com... > I find rule #2 jarring... it runs contrary to my understanding of how > inheritance should work, let alone overloading. I had always thought > that if I were to declare: > > public class Class1 > { > public void Method1(string s) {...} > } > > public class Class2 : Class1 > { > public void Method1(object o) {..} > } > > and called > > Class2 c2 = new Class2(); > c2.Method1("Hello world"); > > then of course the method invoked would be the method from the base > class, Class1. You're telling me that the Class2 method would be > invoked instead? That's loopy! > That makes more sense, now. I'm still not 100% sold, but I know that
the .NET Framework designers spent a lot of time thinking about versioning, the contracts implied by class interfaces, and not breaking derived classes / clients just because a base class changed. That's one of the reasons why checked exceptions weren't included in C#. So, I can see them doing this as well in order to solve similar versioning problems. It still screws up my notions of class inheritance, though. I agree. But the case Bruce introduces is different.
Show quote "James Curran" <jamescur***@mvps.org> wrote in message news:ObULoC5PFHA.2604@TK2MSFTNGP10.phx.gbl... > No, that makes sense (It's also how it done in C++). Consider if you > had written this without Class1.Method1. Naturally, you'd expect > Class2.Method1 to be called, as it's the only Method1. Now consider if, > years later, some maintence programmer added Method1 to Class1. Next time > you recompile your code, suddenly a different function is being called. > That's not the way it should work. So, method1 in a derived class hide base > methods with the same name. > > > > "Bruce Wood" <brucew***@canada.com> wrote in message > news:1113324434.303178.152200@g14g2000cwa.googlegroups.com... > > I find rule #2 jarring... it runs contrary to my understanding of how > > inheritance should work, let alone overloading. I had always thought > > that if I were to declare: > > > > public class Class1 > > { > > public void Method1(string s) {...} > > } > > > > public class Class2 : Class1 > > { > > public void Method1(object o) {..} > > } > > > > and called > > > > Class2 c2 = new Class2(); > > c2.Method1("Hello world"); > > > > then of course the method invoked would be the method from the base > > class, Class1. You're telling me that the Class2 method would be > > invoked instead? That's loopy! > > > > > You're telling me that the Class2 method would be I had to adjust my own thinking a bit when I researched these rules. They do > invoked instead? That's loopy! make sense, they just don't match everyone's initial intuition. It helped me to replace Class1 and Class2 with Person and Employee. If we have an Employee object it sounds reasonable to say "Employee methods are preferred over Person methods". Mark Hi,
I agree that candidates must come from the same class. Otherwise we'll fall into the problem described by James. But I cannot agree that the overrides should be excluded from the candidate list. Also the argument that they are logically same as the original in the base class I think is weak. When a derived class overrides the method in a base class, it states that he know how to perform given operation (even better, with or without the help of the base class). That is the override method should be considered as it comes from the derived class. We cannot find any solid argument that they are not from it. > "Employee methods are preferred over Person methods". MarkT, I understant what do you mean here, but I think, that through overeload resolution process Employee methods shoud include the overrides too. Otherwise, as it is in fact, the overload resolution cuts off the chance dynamic binding to do it's job.I have coped with the task to prepare a managed C++ example (included below). The "problem" is missing there. It's looks like only C# is so "smart" in overload resolution. I really like C# and I've never preffered any other language. But this is very concerning. #include "stdafx.h" #using <mscorlib.dll> using namespace System; using namespace System::Diagnostics; __gc public class CClass1 { public: virtual void Method1(String *s) { System::Diagnostics::Trace::WriteLine("Class1.Method1"); } }; __gc public class CClass2 : public CClass1 { public: void Method1(String *s) { System::Diagnostics::Trace::WriteLine("Class2.Method1a"); } void Method1(Object *o) { System::Diagnostics::Trace::WriteLine("Class2.Method1b"); } }; int _tmain() { CClass2* o2 = new CClass2(); o2->Method1("Hello World!"); return 0; } Show quote "MarkT [developmentor]" <MarkTdevelopmen***@discussions.microsoft.com> wrote in message news:22140A2F-3B14-493F-B2AB-D7E9E36C71B9@microsoft.com... > > You're telling me that the Class2 method would be > > invoked instead? That's loopy! > > I had to adjust my own thinking a bit when I researched these rules. They do > make sense, they just don't match everyone's initial intuition. > > It helped me to replace Class1 and Class2 with Person and Employee. If we > have an Employee object it sounds reasonable to say "Employee methods are > preferred over Person methods". > > Mark I've observed the behaviour you second point refers to, but can't find it in
ECMA334. Do you by any chance know which section it is in? Thanks, Aaron. Show quote "MarkT [developmentor]" wrote: > 2. All candidate methods must come from the same type. We use the lowest > type in the hierarchy that contains an applicable method. We would prefer to > select a method from the lowest type since it is the most specific and we > assume it would perform the most appropriate operation. Only if the lowest > type does not contain an applicable method do we look further up the > hierarchy. We keep all the methods declared in that type and discard all > methods declared in all other types. We discard methods in the other types > even if their parameter list is a better match. Hi everybody,
At first thank you all for the interesting and reasonable responses. It seems that compiler beheaves regarding the specification. Yet yesterday I tried to read and understand the spec, but left it after getting headache. :) As MarkT says "The full story of overload resolution is incredibly complicated ..." Anyway, do you think that this behaviour is right even if it is correct regarding the specification. Shoud we report this and ask for specification changes ? In between I tryed to test the case in VB.NET, and just want to let you know that the "problem" does not apear there (You may try the code below). A colleague of mine says that this "wrong" behaviour is missing in C++. Cheers, Vladimir Class Class1 Public Overridable Sub Method1(ByVal s As String) Trace.WriteLine("Class1.Method1") End Sub End Class Class Class2 Inherits Class1 Public Overloads Overrides Sub Method1(ByVal s As String) Trace.WriteLine("Class2.Method1a") End Sub Public Overloads Sub Method1(ByVal o As Object) Trace.WriteLine("Class2.Method1b") End Sub End Class Module Module1 Sub Main() Dim o2 As Class2 = New Class2 Dim s As String = "blah" o2.Method1(s) End Sub End Module "Vladimir Granitsky" <vl_granit***@hotmail.com> wrote in message news:%23%232N1G1PFHA.2788@TK2MSFTNGP09.phx.gbl... Please, look at the code below and try to step into it. The compiled code calls the loosely typed method public void Method1(object o) !?!?Hi guys, Am I right that C# compiler does wrong overload resolution ? I've used parameters of type object and string here, just to illustrate the problem. Really I have a bit more deep inheritance graph, and the things get more interesting if the strongly typed overload is like override public void Method1(BaseType x). When I call it with parameter of type SubType (that inherits BaseType) the right method is called. Thanks for any useful points. Regadrs, Vladimir Granitsky using System;using System.Diagnostics;namespace OverloadResolution{ public class Class1 { virtual public void Method1(string s) { Trace.WriteLine("Class1.Method1"); } } public class Class2 : Class1 { override public void Method1(string s) { Trace.WriteLine("Class2.Method1a"); } public void Method1(object o) { Trace.WriteLine("Class2.Method1b"); } } class Client { [STAThread] static void Main(string[] args) { string s = "blah"; Class2 o2 = new Class2(); o2.Method1(s); } }} |
|||||||||||||||||||||||