473,387 Members | 1,530 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

delegate contravariance not fully implemented

I've extended the article "Covariance and Contravariance in Delegates" from
the C# Programmers' Guide, and it seems contravariance of delegates is
implemented wrong.

Lambda calculus states that Handler2Method is a subtype of HandlerMethod...
why can't I cast (it should be implicit conversion, actually)? However the
last line does work, but I'm not wanting
to create new delegate objects. I'm actually implementing contravariant
events, and I'm scared about my remove implementation. In

event BaseInterface.baseEvent {
add { derivedEvent += new DerivedDelegate(value); } // add a new
handler, that's ok
remove { derivedEvent -= new DerivedDelegate(value); } // I don't want
to remove a new handler, I want to remove the one I added on the line above!
}

Is removal going to work, or have I created a "new" object that won't match
the existing one????

HELP!


class Mammals
{
}

class Dogs : Mammals
{
}

class Program
{
public delegate void HandlerMethod(Dogs sampleDog);
public delegate void Handler2Method(Mammals sample);

public static void FirstHandler(Mammals elephant)
{
}

public static void SecondHandler(Dogs sheepDog)
{
}

static void Main(string[] args)
{
// Contravariance permits this delegate.
HandlerMethod handler1 = FirstHandler;

HandlerMethod handler2 = SecondHandler;

Handler2Method handler3 = FirstHandler; // ok

HandlerMethod handler4 = handler3; // error CS0029: Cannot
implicitly convert type 'Program.Handler2Method' to 'Program.HandlerMethod'

HandlerMethod handler5 = (HandlerMethod)handler3; // error CS0030:
Cannot convert type 'Program.Handler2Method' to 'Program.HandlerMethod'

HandlerMethod handler6 = new HandlerMethod(handler3); // ok
}
}


Mar 22 '06 #1
6 1337
Hi Ben,

Based on my research, when we try to call the code below, the compile helps
to generate the code that created the delegate.
HandlerMethod handler1 = FirstHandler;
That is to say , the codeline 1 and 2 below will do the same job.
HandlerMethod handler1 = FirstHandler;
HandlerMethod hTest = new HandlerMethod(FirstHandler);
handler1 = hTest;

We can look into the IL code which is the .NET assembly code.
Notice that there will call twice newobj instructions.
That is to say, "HandlerMethod handler1 = FirstHandler;" is an constructor
behavior but not a simple assignment behavior just as code line 3 do.
So code 3 will convert type check.
If the handler1 and hTest is not compatible, the convert will fail. That is
why you will get the error.

method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 30 (0x1e)
.maxstack 3
.locals init ([0] class TestDelegate.Program/HandlerMethod handler1,
[1] class TestDelegate.Program/HandlerMethod hTest)
IL_0000: nop
IL_0001: ldnull
IL_0002: ldftn class TestDelegate.Mammals
TestDelegate.Program::FirstHandler()
IL_0008: newobj instance void
TestDelegate.Program/HandlerMethod::.ctor(object,

native int)
IL_000d: stloc.0
IL_000e: ldnull
IL_000f: ldftn class TestDelegate.Mammals
TestDelegate.Program::FirstHandler()
IL_0015: newobj instance void
TestDelegate.Program/HandlerMethod::.ctor(object,

native int)
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: stloc.0
IL_001d: ret
} // end of method Program::Main

Also you may take a look at the code below.
using System;
using System.Collections.Generic;
using System.Text;

namespace TestDelegate
{
public class EventSource2
{
public delegate Mammals TestEventDelegate();
public TestEventDelegate TestEventHandlers;

public event TestEventDelegate TestEvent
{
add
{
TestEventHandlers += value;

}
remove
{
TestEventHandlers -= value;

}
}
public void RaiseTestEvent()
{
// Safely invoke an event.
TestEventDelegate temp = TestEventHandlers;

if (temp != null)
{
temp();
}
}
}

public class Mammals
{
}

public class Dogs : Mammals
{
}

public class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();
public static Mammals FirstHandler()
{
Console.WriteLine("FirstHandler");
return null;
}

public static Dogs SecondHandler()
{
Console.WriteLine("SecondHandler");
return null;
}

static void Main()
{
EventSource2 es = new EventSource2();
es.TestEvent += new
EventSource2.TestEventDelegate(FirstHandler);
es.TestEvent += new
EventSource2.TestEventDelegate(SecondHandler);
es.RaiseTestEvent();
es.TestEvent -= new
EventSource2.TestEventDelegate(FirstHandler);
es.RaiseTestEvent();
es.TestEvent -= new
EventSource2.TestEventDelegate(SecondHandler);
es.RaiseTestEvent();
//HandlerMethod handler1 = FirstHandler;
//HandlerMethod hTest = new HandlerMethod(FirstHandler);
//handler1 = hTest;
}
}

}
Best regards,

Peter Huang

Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

Mar 23 '06 #2
Ben Voigt wrote:
I've extended the article "Covariance and Contravariance in Delegates" from
the C# Programmers' Guide, and it seems contravariance of delegates is
implemented wrong.

Lambda calculus states that Handler2Method is a subtype of HandlerMethod...


(For reference, here are the definitions:
public delegate void HandlerMethod(Dogs sampleDog);
public delegate void Handler2Method(Mammals sample);
)

I don't know about Lambda calculus, but that's certainly not what the
C# spec says. For one thing, I think you've stated it the wrong way
round - HandlerMethod is compatible with the signature (Mammals) but
Handler2Method isn't compatible with the signature (Dogs). In other
words, one might expect to be able to do:

HandlerMethod m1 = ...;
Handler2Method m2 = m1;

but not the other way round - because it's reasonable to call a
Handler2Method with a Dogs, but it's *not* reasonable to call a
HandlerMethod with any old Mammals (eg new Cats()).

Now, that's covariance between a method group and a delegate.
Unfortunately, it doesn't look like the C# spec allows for actual
delegate instances to be converted at all, as far as I can see. Is that
what your issue really is?

Jon

Mar 23 '06 #3
<snip>
event BaseInterface.baseEvent {
add { derivedEvent += new DerivedDelegate(value); } // add a new
handler, that's ok
remove { derivedEvent -= new DerivedDelegate(value); } // I don't
want
to remove a new handler, I want to remove the one I added on the line
above!
}

Is removal going to work, or have I created a "new" object that won't
match the existing one????

<snip>

Removal will work. The .remove method on the event knows how to match this
and will not look at the delegate object itself, but use the target object
and method to match against existing delegates.

In short, the above syntax is the right one.

--
Lasse Vågsæther Karlsen
http://usinglvkblog.blogspot.com/
mailto:la***@vkarlsen.no
PGP KeyID: 0x2A42A1C2
Mar 23 '06 #4

"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:11**********************@u72g2000cwu.googlegr oups.com...
Ben Voigt wrote:
I've extended the article "Covariance and Contravariance in Delegates"
from
the C# Programmers' Guide, and it seems contravariance of delegates is
implemented wrong.

Lambda calculus states that Handler2Method is a subtype of
HandlerMethod...
(For reference, here are the definitions:
public delegate void HandlerMethod(Dogs sampleDog);
public delegate void Handler2Method(Mammals sample);
)

I don't know about Lambda calculus, but that's certainly not what the
C# spec says. For one thing, I think you've stated it the wrong way
round - HandlerMethod is compatible with the signature (Mammals) but
Handler2Method isn't compatible with the signature (Dogs). In other
words, one might expect to be able to do:

HandlerMethod m1 = ...;
Handler2Method m2 = m1;

but not the other way round - because it's reasonable to call a
Handler2Method with a Dogs, but it's *not* reasonable to call a
HandlerMethod with any old Mammals (eg new Cats()).


Exactly why the assignment above is not permissible.

m1 can be set to a method that requires a Dogs.
m2 can be called with a mammals.
Therefore m2 = m1 is illegal.

However, m2 can only be set to elements that accept any subtype of Mammals
m1 can only be called with a Dogs
Therefore m1 is a subtype of m2, you can always call m2 handlers in an m1
event.

Now, that's covariance between a method group and a delegate.
But subtyping is covariant on return type and contravariant on arguments.
Unfortunately, it doesn't look like the C# spec allows for actual
delegate instances to be converted at all, as far as I can see. Is that
what your issue really is?

Jon

Mar 23 '06 #5
Ben Voigt <bv****@nospam.nospam> wrote:

<snip>

(Jon wrote:)
In other words, one might expect to be able to do:

HandlerMethod m1 = ...;
Handler2Method m2 = m1;

but not the other way round - because it's reasonable to call a
Handler2Method with a Dogs, but it's *not* reasonable to call a
HandlerMethod with any old Mammals (eg new Cats()).


Exactly why the assignment above is not permissible.


Whoops - yes, I see what you mean. Sorry about that - that's what comes
of posting before work :(

Anyway, back to your previous question - is your issue that there's no
conversion between actual delegates, i.e. the co/contravariance is only
applicable from a method group to a delegate type?

--
Jon Skeet - <sk***@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
Mar 23 '06 #6
Thanks, that was exactly what I needed to know.... a little
counter-intuitive though.

"Lasse Vågsæther Karlsen" <la***@vkarlsen.no> wrote in message
news:a0*************************@news.microsoft.co m...
<snip>
event BaseInterface.baseEvent {
add { derivedEvent += new DerivedDelegate(value); } // add a new
handler, that's ok
remove { derivedEvent -= new DerivedDelegate(value); } // I don't
want
to remove a new handler, I want to remove the one I added on the line
above!
}

Is removal going to work, or have I created a "new" object that won't
match the existing one????

<snip>

Removal will work. The .remove method on the event knows how to match this
and will not look at the delegate object itself, but use the target object
and method to match against existing delegates.

In short, the above syntax is the right one.

--
Lasse Vågsæther Karlsen
http://usinglvkblog.blogspot.com/
mailto:la***@vkarlsen.no
PGP KeyID: 0x2A42A1C2

Mar 23 '06 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Tony Johansson | last post by:
Hello!! Can somebody explain what does argument contravariance mean. I know what covariance mean because this is involved in some way when we have argument contravariance . Use some easy code...
6
by: Ondrej Sevecek | last post by:
Hello, what is the difference between "event" and the only use of delegate? Why one should note events with "event" keyword when the functionality seems the same as with pure delegates? When I...
1
by: Daniel | last post by:
How do I call a delegate in late bound C# dll? there some way to do this w/ a sharedinterface file? any examples? i tried this but it doesnt work: (oType.GetMethod("IOCTLJOB").Invoke(pObj, new...
0
by: Wavemaker | last post by:
One of the things I've struggled with from time to time is handling events raised on different threads. For example, an object could be listening to events from one or more objects running in...
3
by: Jeff S | last post by:
Please consider this sample code: It registers a delegate with an event. p1.FirstNameChanged += new Person.NameChanged(p1_FirstNameChanged); Now the following code removes the delegate:...
7
by: Ant | last post by:
Hello, Very simple question but one I need clarified. Which part of the statement below is considered the 'delegate'? Is it the 'new System.EventHandler' or the btnAccept_Click? or is it...
1
by: Ben Voigt | last post by:
I've extended the article "Covariance and Contravariance in Delegates" from the C# Programmers' Guide, and it seems contravariance of delegates is implemented wrong. Lambda calculus states that...
1
by: =?Utf-8?B?aGVyYmVydA==?= | last post by:
Jon Skeet wrote in the .NET general newsgroup 11/17/2005: "The difference between Invoke and DynamicInvoke is that the parameters to Invoke depend on the delegate itself - the method has the same...
3
by: =?Utf-8?B?RnJhbmsgVXJheQ==?= | last post by:
Hi all I have some problems with Crystal Reports (Version 10.2, Runtime 2.0). In Section3 I have added a OLE Object (Bitmap). Now when I open the report in my code I would like to set this...
3
by: Anders Borum | last post by:
Hello, I've worked on an API for quite some time and have (on several occasions) tried to introduce generics at the core abstract level of business objects (especially a hierarchical node). The...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.