When Overloading the Plus Operator, What are Valid Arguments Types?

J

jehugaleahsa

Hello:

Today I learned that the + operator cannot be passed a delegate. I get
an error from the CLR saying I have an invalid program.

With that, I was wondering if someone could tell me what other types I
am not allowed to make parameters.

Thanks,
Travis

class Failer
{
public static Failer operator +(Failer failer,
MulticastDelegate handler)
{
return failer;
}

public static void Test()
{
Failer failer = new Failer();
object o = failer + new EventHandler(
delegate(object sender, EventArgs e)
{
// DO NOTHING
});
}
}
 
J

jehugaleahsa

Of course It will work if you change my code. Go back to what I sent
and it will fail.
 
J

Jeroen Mostert

Today I learned that the + operator cannot be passed a delegate. I get
an error from the CLR saying I have an invalid program.
Congratulations, you've discovered a bug! Step right up to Microsoft Connect
to earn your prize...
With that, I was wondering if someone could tell me what other types I
am not allowed to make parameters.
It's not that, the compiler is emitting invalid IL, and the CLR barfs when
it encounters it.
class Failer
{
public static Failer operator +(Failer failer,
MulticastDelegate handler)
{
return failer;
}

public static void Test()
{
Failer failer = new Failer();
object o = failer + new EventHandler(
delegate(object sender, EventArgs e)
{
// DO NOTHING
});
}
}

Change this to

object o = failer + delegate...

That is, simply omit the explicit instantiation, and it will work. It will
also work if you use a method rather than an anonymous delegate. Your
original code is failing because it compiles to something like this:

..method public hidebysig static void Test() cil managed
{
// Code size 15 (0xf)
.maxstack 1
.locals init ([0] class ConsoleApplication2.Failer failer,
[1] object o)
IL_0000: nop
IL_0001: newobj instance void ConsoleApplication2.Failer::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: call class ConsoleApplication2.Failer
ConsoleApplication2.Failer::blush:p_Addition(class ConsoleApplication2.Failer,

class [mscorlib]System.EventHandler)
IL_000d: stloc.1
IL_000e: ret
} // end of method Failer::Test

The call at IL_0008 is invalid because there's only one argument on the
stack as opposed to the two which are required. There's something going
wrong here with the magic the compiler uses to create event handlers from
delegates -- the required code is completely missing!

The corrected code looks like this:

..method public hidebysig static void Test() cil managed
{
// Code size 46 (0x2e)
.maxstack 4
.locals init ([0] class ConsoleApplication2.Failer failer,
[1] object o)
IL_0000: nop
IL_0001: newobj instance void ConsoleApplication2.Failer::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldsfld class [mscorlib]System.EventHandler
ConsoleApplication2.Failer::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_000d: brtrue.s IL_0022
IL_000f: ldnull
IL_0010: ldftn void ConsoleApplication2.Failer::'<Test>b__0'(object,
class
[mscorlib]System.EventArgs)
IL_0016: newobj instance void
[mscorlib]System.EventHandler::.ctor(object,

native int)
IL_001b: stsfld class [mscorlib]System.EventHandler
ConsoleApplication2.Failer::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0020: br.s IL_0022
IL_0022: ldsfld class [mscorlib]System.EventHandler
ConsoleApplication2.Failer::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0027: call class ConsoleApplication2.Failer
ConsoleApplication2.Failer::blush:p_Addition(class ConsoleApplication2.Failer,

class [mscorlib]System.EventHandler)
IL_002c: stloc.1
IL_002d: ret
} // end of method Failer::Test

You see the boilerplate here between IL_0008 and IL_0022 for creating your
event handler and loading it from a static field.
 
M

Marc Gravell

Good analysis - one point, though:

"Change this to object o = failer + delegate..."

With the code as originally presented (MulticastDelegate), you'd need
to type the delegate:

o = failer + (EventHandler) delegate {...};

Of course, a better option would be to declare something more specific
for the "del" argument - perhaps using the Action<...> or Func<...>
family to clarify the intent (EventHandler is fine too...).

Marc
 
J

Jeroen Mostert

Marc said:
Good analysis - one point, though:

"Change this to object o = failer + delegate..."

With the code as originally presented (MulticastDelegate), you'd need
to type the delegate:

o = failer + (EventHandler) delegate {...};
Oops, you're right, of course. Changing around things a little too
enthusiastically to pin down the root cause.
Of course, a better option would be to declare something more specific
for the "del" argument - perhaps using the Action<...> or Func<...>
family to clarify the intent (EventHandler is fine too...).
Yes, using MulticastDelegate is pretty fishy -- I can't remember a single
occasion where I had a need to explicitly declare that.
 
J

jehugaleahsa

Oops, you're right, of course. Changing around things a little too
enthusiastically to pin down the root cause.


Yes, using MulticastDelegate is pretty fishy -- I can't remember a single
occasion where I had a need to explicitly declare that.

Thank you. Someone who actually looks at the code I posted, instead of
just bashing me. It is nice to know that some people try to help
rather than always question the questions.
 
J

jehugaleahsa

I demonstrated a concise-but-complete code sample that overloads the +  
operator with a MulticastDelegate argument.  That was the only complaint  
in your original post.

I don't see any material differences between the code you posted and mine,  
except that mine can be compiled and run and yours can't.  But even if  
there are material differences that I've overlooked, your question as  
stated has been refuted by my example (you obviously can overload the +  
operator with a MulticastDelegate argument), and you should not expect  
people to bother compiling your code when you haven't provided a complete,  
compilable code sample.

Pete

So, in other words, Pete, you don't know why my code doesn't run. The
fact of the matter is that I see your code and mine as somewhat
functionaly equivilent (aside from mine wrapping the anonymous method
inside of a delegate and yours simply using the anonymous method as
is).

For a typical programmer, the difference doesn't seem significant
enough to cause an InvalidProgramException. I was hoping someone could
explain why the difference would cause the error, regardless what my
original, uneducated post asked.

Thankfully, my real question was answered without being asked
directly. Some times asking the right question is harder than finding
the answer.
 
M

Marc Gravell

An interesting bug ;-p

If you do log it on connect, I'll happily click the "validated" button -
'tis definitely a compiler bug.

Marc
 
J

Jon Skeet [C# MVP]

On Sep 12, 2:32 pm, "(e-mail address removed)" <[email protected]>
wrote:

Thank you. Someone who actually looks at the code I posted, instead of
just bashing me. It is nice to know that some people try to help
rather than always question the questions.

It's not at all unreasonable to ask for code which is complete,
compiles, and demonstrates the problem. Yes, sometimes (as in this
case) it's possible for someone to modify the incomplete code when
it's presented such that it compiles and shows the described problem -
but it's *much* better to ask the question with code which compiles in
the first place. Being rude to someone who is trying to help (but
hasn't managed to reproduce your error) only serves to discourage
people from trying to help you in the future.

Jon
 
J

Jeroen Mostert

Peter said:
[...]
Thankfully, my real question was answered without being asked
directly. Some times asking the right question is harder than finding
the answer.

Yes, you were fortunate that Jeroen was nice enough to do the extra work
you should have done yourself by converting your code sample into
something that could actually compile. You were also fortunate that
having done so, he was able to reproduce the same problem you found.

I'd like to point out that the only reason I did this was because an
InvalidProgramException always indicates a compiler bug (the compiler should
never produce a program that causes this)! If your problem had been less
interesting, I might not have bothered. Ditto if I wouldn't have been able
to reproduce it.

The fact that my analysis helped you should be considered a side effect -- I
wasn't actually helping you so much as satisfying my own curiosity.

Just to put things in perspective as to who was putting in effort to what end...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top