.NET Forum / Languages / C# / July 2007
this should compile but doesn't
|
|
Thread rating:  |
not_a_commie - 05 Jul 2007 23:08 GMT Can anyone tell me what's wrong with the following code? It doesn't compile. The compiler tries to use the wrong overload. Is it a known bug? Thanks for your time.
using System; using System.Collections.Generic; using System.Text; using System.Xml;
namespace BadOverloads { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)] public abstract class XmlFormatterAttribute : Attribute { public abstract string Format(object value);
public abstract object Unformat(string value); }
public class Program {
public void func(XmlDocument document, XmlFormatterAttribute attribute) { Console.Out.WriteLine("func1"); }
public void func(XmlDocument document, ref object value) { Console.Out.WriteLine("func2:" + value.ToString()); }
static void Main(string[] args) { //object testobj = new object(); //func(new XmlDocument(), ref testobj); // this doesn't work either
string str = "howdy"; func(new XmlDocument(), ref str); } } }
not_a_commie - 05 Jul 2007 23:12 GMT In the example code I posted, the two func overloads should be static. That doesn't fix the problem, though. I find it fascinating that you get the same error whether or not they're static.
Peter Duniho - 05 Jul 2007 23:27 GMT > Can anyone tell me what's wrong with the following code? It doesn't > compile. The compiler tries to use the wrong overload. Is it a known > bug? Thanks for your time. I don't think this has anything to do with the abstract class or the overloading. As such, an appropriate, minimal code sample would not include them. Please try to post minimal code examples, as otherwise there's a lot of other crud distracting from the issue.
As far as the actual question goes, if you use "ref" or "out" I believe that the type has to match exactly. I presume that this is because if the types don't match, an implicit cast has to be made somewhere, but the parameter being a reference to some existing variable would not be cast in a context where it's simple for the compiler to know it needs to be cast (i.e. it would be a run-time thing, too late for the compiler to include the implicit cast).
Pete
Jon Skeet [C# MVP] - 05 Jul 2007 23:29 GMT > Can anyone tell me what's wrong with the following code? It doesn't > compile. The compiler tries to use the wrong overload. Is it a known > bug? Thanks for your time. It shouldn't compile, even if func is static.
The problem is that you're trying to use "ref str" as the argument for a parameter which is declared as "ref object". You can't do that, because the method could do:
value = new object();
at which point you'd be stuffed.
I suspect you don't quite understand ref parameters. See http://pobox.com/~skeet/csharp/parameters.html
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too
Peter Duniho - 06 Jul 2007 00:37 GMT > The problem is that you're trying to use "ref str" as the argument for > a parameter which is declared as "ref object". You can't do that, [quoted text clipped - 6 lines] > I suspect you don't quite understand ref parameters. See > http://pobox.com/~skeet/csharp/parameters.html For what it's worth, I feel like I understand by-value and by-reference very well, and I still had to think about the question for a little bit, and I still have to do some hand-waving to explain why C# doesn't support the scenario.
In particular, take your example "value = new object()" when the parameter passed was actually a string. C# _could_ allow for including type information for the original reference when passing by reference, doing a run-time cast on any assignments and throwing an exception if the cast wasn't successful. More complicated? Yes...a lot more complicated. But I don't see anything that fundamentally prevents that scenario.
Similarly, going the other way -- passing a "ref object" when the method is declared "ref string" -- the compiler could verify that the "object" typed variable held a string before the call, and allow that if it was. Even easier, for the "out" keyword, the compiler could just not care, since the rules require the parameter to be assigned in the method before use anyway (so as long as the passed in variable statically typed to something that could hold the method's parameter type, that should be okay).
Now, my understanding is that none of this happens. But it could.
In other words, the restriction that when passing parameters using "ref" or "out" the passed variable type needs to match exactly the parameter type is somewhat arbitrary. Worse, it is not documented _at all_. I agree that the restriction can be inferred by thinking about the consequences of not having it, but there's nothing in any of the documentation I looked at (this includes MSDN's doc pages for the "ref" and "out" keywords, your "parameters.html" web page, and the two pages you refer to in that page) that explicitly states the documentation.
Pete
Marc Gravell - 06 Jul 2007 05:25 GMT > Yes...a lot more complicated. But > I don't see anything that fundamentally prevents that scenario. I actually appriate the choice of "keep it simple". For more complicated scenarios the caller has the capacity (in the object case) of simply declaring an object variable locally for the duration of the call, i.e.
string s = "abc"; object obj = s; // perhaps replace with some other base-class SomeMethod(ref obj); s = (string) obj; // cross fingers...
Not as succinct, but simple to understand (IMO), plus it can only break at a point known to the author (which is reasonable, as they are the ones forcing a square peg into a round hole) - where-as silent internal casts could start breaking randomly if the SomeMethod author changes the implementation.
Marc
Peter Duniho - 06 Jul 2007 09:40 GMT >> Yes...a lot more complicated. But >> I don't see anything that fundamentally prevents that scenario. > > I actually appriate the choice of "keep it simple". I do too. I certainly did not mean to imply that I disagree with the design.
I'm just pointing out that this is actually not a very well-documented aspect of the language, IMHO. While, as I said, one can infer the behavior by considering the implications of the compiler allowing what the OP thinks should be allowed, it's definitely not explicitly stated anywhere I looked.
Pete
not_a_commie - 06 Jul 2007 16:40 GMT > string s = "abc"; > object obj = s; // perhaps replace with some other base-class > SomeMethod(ref obj); > s = (string) obj; // cross fingers... So is the last line of that strictly necessary or only in the case of value types?
Jon Skeet [C# MVP] - 06 Jul 2007 16:46 GMT > > string s = "abc"; > > object obj = s; // perhaps replace with some other base-class [quoted text clipped - 3 lines] > So is the last line of that strictly necessary or only in the case of > value types? It's necessary if you want to use the "s" variable to hold the new value. (And string isn't a value type.)
Jon
Jon Skeet [C# MVP] - 06 Jul 2007 07:46 GMT <snip>
> Now, my understanding is that none of this happens. But it could. Well, possibly. The runtime would have to pass the type of the variable instead of just a pointer etc...
> In other words, the restriction that when passing parameters using "ref" > or "out" the passed variable type needs to match exactly the parameter > type is somewhat arbitrary. Worse, it is not documented _at all_. It is - it's in the C# language specification.
> I agree that the restriction can be inferred by thinking about the > consequences of not having it, but there's nothing in any of the > documentation I looked at (this includes MSDN's doc pages for the "ref" > and "out" keywords, your "parameters.html" web page, and the two pages you > refer to in that page) that explicitly states the documentation. Here's a quote from the C# spec, section 14.4.2.1:
<quote> For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical to the parameter passing mode of the corresponding parameter, and
for a value parameter or a parameter array, an implicit conversion (§13.1) exists from the type of the argument to the type of the corresponding parameter, or
for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter. </quote>
Unless you meant that the reasons aren't explicitly documented - in which case I agree. From memory, it's likely to be in the C# Annotated Standard which is out soon:
http://www.amazon.com/C-Annotated-Standard-Jon- Jagger/dp/0123725119/ref=sr_1_1/103-6108362-5713441?ie=UTF8 &s=books&qid=1183704314&sr=8-1
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too
Peter Duniho - 06 Jul 2007 09:47 GMT >> In other words, the restriction that when passing parameters using "ref" >> or "out" the passed variable type needs to match exactly the parameter >> type is somewhat arbitrary. Worse, it is not documented _at all_. > > It is - it's in the C# language specification. Okay, perhaps "at all" was over-reaching.
However, most people don't read the spec, they read the user reference materials on MSDN. And I do think it's worth pointing out that your own page on parameters, which is otherwise a good read, doesn't address this at all. Not directly (though as I pointed out, one can infer the rules based on the basic behavior of "ref" and "out", at least if some basic assumptions are made).
It's my opinion that the MSDN pages that document the "ref" and "out" keywords should both specifically say that there's a requirement that the types match exactly. For bonus points, the page discussing "Passing Parameters (C#)" would also mention that requirement.
Pete
Jon Skeet [C# MVP] - 06 Jul 2007 10:09 GMT On Jul 6, 9:47 am, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > It is - it's in the C# language specification. > [quoted text clipped - 6 lines] > based on the basic behavior of "ref" and "out", at least if some basic > assumptions are made). Righto - I can fix that easily enough. You're right - it's certainly an omission.
> It's my opinion that the MSDN pages that document the "ref" and "out" > keywords should both specifically say that there's a requirement that the > types match exactly. For bonus points, the page discussing "Passing > Parameters (C#)" would also mention that requirement. Can't fix that :( However, you could certainly mail the MSDN maintainers - click on the link at the bottom of the page and give the appropriate feedback. It's worked for me a couple of times.
Jon
Christof Nordiek - 06 Jul 2007 10:09 GMT >>> In other words, the restriction that when passing parameters using "ref" >>> or "out" the passed variable type needs to match exactly the parameter [quoted text clipped - 10 lines] > based on the basic behavior of "ref" and "out", at least if some basic > assumptions are made). Actually I searched the programmers reference in MSDN for this issue, and I didn't even find anything about overload resolution and wich types can be used in a method call. That docs are sometimes are very poor, regarding such definitions. But I agree it should be stated there, atleast in a way, that the most important implication can be induced by it very intuitivly.
Christof
not_a_commie - 06 Jul 2007 16:43 GMT > Actually I searched the programmers reference in MSDN for this issue, and I > didn't even find anything about overload resolution and wich types can be > used in a method call. That docs are sometimes are very poor, regarding such > definitions. But I agree it should be stated there, atleast in a way, that > the most important implication can be induced by it very intuitivly. I think it should be stated clearly in the error message. It appears, though, that the compiler attempts to use the overloads, though, even though they aren't a close match, thus obfuscating the original error. This is particularly confusing for the users.
Peter Duniho - 06 Jul 2007 17:30 GMT > I think it should be stated clearly in the error message. It appears, > though, that the compiler attempts to use the overloads, though, even > though they aren't a close match, thus obfuscating the original error. > This is particularly confusing for the users. The compiler will mention "overload" even if there's only one version of the method. I'd agree this is misleading, but it's not unique to this particular situation.
I have, by now, been trained to just ignore a mention of "overload", and look at the second error generated (they almost always come in pairs :) ).
Pete
not_a_commie - 06 Jul 2007 16:37 GMT > Now, my understanding is that none of this happens. But it could. Thanks Pete. I like you're thinking on this. My $0.02 on making the compiler handle this: if you can justify immutable variable types behind the scenes, you can justify autocasting in this case as well.
Jon Skeet [C# MVP] - 06 Jul 2007 16:56 GMT > > Now, my understanding is that none of this happens. But it could. > > Thanks Pete. I like you're thinking on this. My $0.02 on making the > compiler handle this: if you can justify immutable variable types > behind the scenes, you can justify autocasting in this case as well. But the method being called doesn't know (normally) what the type of the original variable is - that's part of the problem.
It could cause some very odd-looking bugs, with an assignment which looks like it should just work failing at runtime.
Jon
Peter Duniho - 06 Jul 2007 17:35 GMT >> Now, my understanding is that none of this happens. But it could. > > Thanks Pete. I like you're thinking on this. My $0.02 on making the > compiler handle this: if you can justify immutable variable types > behind the scenes, you can justify autocasting in this case as well. Well, just to be clear: "my thinking" in this context (that is, the post to which you replied) isn't about what I think _should_ happen. It's about what I think _could_ happen.
There would be a lot more overhead involved in handling the type coercion in the way that I describe, since type information would have to be passed along with the variable itself, and it would introduce new ways for unexpected runtime errors to occurs.
I don't feel that any of this is warranted, since there's an easy workaround to the issue: just make sure the types match, and do any necessary type-casting explicitly as needed. IMHO, that provides a more-consistent "compiler experience", and is likely to reduce bugs in the code as well, since everything has to be spelled out explicitly.
I'm not sure what you mean by "immutable variable types behind the scenes". I'm not aware of any "behind the scenes immutable variables types". But whatever you mean by that, I'm not sure that it leads to a logical conclusion that autocasting by-reference parameters is justified.
Pete
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|