Comparing delegates

G

Guest

I'm having some problems comparing delegates. In all sample projects I
create, I can't get the problem to occur, but there is definitely a problem
with my production code.

I can't give all the code, as there's simply too much, but here's the
general gist:

I have a connection object which connects to a custom back-end server of one
type or another. Clients of this connection object send requests via a
method (e.g. connection.SendRequest()) and one of parameters to this call is
a callback delegate.

At a later point, the client may attempt to cancel all the requests specific
to the delegate that was passed in. It's in this method that I'm doing the
delegate comparisons. Consistently, the delegates never match, so the
requests can't be cancelled.

In the client code, I've tried passing in the method name directly or a
delegate instance, created with the method as a parameter. I get the same
results either way.

When debugging, I notice something strange, and no doubt the cause of the
problem:

The stored delegate refers to the actual method to call:
.Method: the actual method name
.Target: the actual class name where the method resides

....but the delegate passed in, refers to an Invoke method:
.Method: Invoke
.Target: the delegate type!

So, I understand why the comparison is failing. They don't appear to be the
same delegate at all! But I don't understand why the passed in delegate
refers to the Invoke method, instead of the actual target method.

Can someone explain to me what's going on? And moreover, does anyone have a
solution to the problem?
Thanks.
 
K

Kevin Spencer

A delegate is actually an instance of the Delegate class, or to be more
specific, to the MulticastDelegate class. It is a very unusual class in that
it represents an instance of a method of another class. It is most like a
function pointer in C or C++, but not really at all like a function pointer.
A function pointer is literally a pointer (memory address) to a function. A
delegate is a class that *encapsulates* a managed pointer to a function.
Hence the name "delegate." The dictionary defines "delegate" as:

A person authorized to act as representative for another; a deputy or an
agent.

Like a human delegate, a Delegate class "represents" or carries out the
duties of the method it encapsulates. Unlike a function pointer, it can
actually represent a multitude of methods, rather than one, hence the name
"multicast." A delegate is actually a linked list of delegates, each of
which represents a method.

The reason you see the method when you debug is because that is what the
delegate is "pointing" to (represents). However, the delegate itself is not
the method, but *invokes* the method when it is used. If it has multiple
methods attached to it, it invokes each in sequence. This is why, for
example, Event Handlers are delegates. Because an event can be subscribed to
by many clients, a single delegate can be used to point to all of the Event
Handlers that subscribe to the notification and receive the parameters
passed when the event is triggered.

There are several ways to compare Delegates. They differ between the .Net
Platform 1.1 and 2.0. I'll assume for now that you're using 2.0. If you use
the Equals method (overridden from Object), 2 Delegates are considered equal
if they are of the same type, and if their targets, methods, and
InvocationLists are equal. That is, if they refer to the same member of the
same class or instance, they are considered equal. However, their
InvocationLists must also be equal. An InvocationList is equal to another if
they contain the same elements in the same order. The InvocationList of a
delegate is a reference to all of the delegates that are part of the same
Multicast delegate. Yes, it's a bit confusing. A delegate actually "points
to" only one method, but it is also a member of a linked list that comprises
a Multicast delegate. There may only be one delegate in the list, if only
one method is assigned to the delegate.

In .Net 1.1, 2 delegates didn't have to be of the same type to be considered
equal, as long as their targets, methods, and InvocationLists were equal.

This comparison can be made using the instance method Equals, or the static
op_Equality method of the delegate class.

The other way to compare 2 delegates is to use the static
Object.ReferenceEquals method. This will return true only if both delegates
are the same instance.

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.
 
G

Guest

Thanks for the background info on delegates; it was helpful.
However, despite this, I still can't solve my problem.

Using the comparison methods you suggested, I get the same results: the
comparisons never return true for the two delegates, despite the fact the I'm
passing the same method name or delegate instance to the different methods of
my connection class.

It's almost as if in the second call (when I'm attempting to cancel the
previous request), .NET is changing the delegate instance to point to Invoke
on the method rather than the method itself. I understand that calling the
delegate would use an Invoke at some point anyway, but then why aren't both
references the same when I compare them? I'll see if I can cut down my code
and post some of it here.
 
G

Guest

Here's the basic structure. Note, however, I don't have a problem with this
code. It appears to be working as expected. When I run the same sort of
structure on my code, the delegates are never equal, as described in my first
post: one is referring to the actual method, one if referring to Invoke.

namespace DelegateTest
{
delegate void MyDelegate(int x);

class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.Start();
}
}

class Client
{
Connection connection = new Connection();

public void Callback(int x)
{
}

public void Start()
{
MyDelegate myDel = new MyDelegate(Callback);

connection.SendRequest(myDel);
connection.SendRequest(Callback);
connection.CancelRequests(myDel);
connection.CancelRequests(Callback);
}
}

class Connection
{
List<MyDelegate> storedDelegates = new List<MyDelegate>();

public void SendRequest(MyDelegate del)
{
storedDelegates.Add(del);
// Send request to server
}

public void CancelRequests(MyDelegate del)
{
Console.WriteLine("Cancel requests:");
int count = 0;
foreach (MyDelegate storedDel in storedDelegates)
{
count++;
Console.WriteLine("Delegate: " + count.ToString());
Console.WriteLine((del == storedDel).ToString());
Console.WriteLine(del.Equals(storedDel).ToString());
Console.WriteLine(MyDelegate.Equals(del,
storedDel).ToString());
Console.WriteLine(MyDelegate.ReferenceEquals(del,
storedDel).ToString());
}
// Send cancel to the server
}
}
}


Any thoughts on why that Invoke is getting in there?
 
B

Barry Kelly

Here's the basic structure. Note, however, I don't have a problem with this
code. It appears to be working as expected. When I run the same sort of
structure on my code, the delegates are never equal, as described in my first
post: one is referring to the actual method, one if referring to Invoke.

I think you're going to have to go through the painful process of
chopping back the code while the problem is still there, until you
have a simple repro which *does* demonstrate the problem.

-- Barry
 
K

Kevin Spencer

your code is a bit confusing. In one case, you're adding a delegate to the
list. In another, you're adding a method:
connection.SendRequest(myDel);
connection.SendRequest(Callback);

They are not the same. A delegate is a delegate; a method is a method.

You have not posted your output, so I have no idea what the answer to the
following question is:
Any thoughts on why that Invoke is getting in there?

What Invoke getting in where?

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.

Quimbly said:
Here's the basic structure. Note, however, I don't have a problem with
this
code. It appears to be working as expected. When I run the same sort of
structure on my code, the delegates are never equal, as described in my
first
post: one is referring to the actual method, one if referring to Invoke.

namespace DelegateTest
{
delegate void MyDelegate(int x);

class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.Start();
}
}

class Client
{
Connection connection = new Connection();

public void Callback(int x)
{
}

public void Start()
{
MyDelegate myDel = new MyDelegate(Callback);

connection.SendRequest(myDel);
connection.SendRequest(Callback);
connection.CancelRequests(myDel);
connection.CancelRequests(Callback);
}
}

class Connection
{
List<MyDelegate> storedDelegates = new List<MyDelegate>();

public void SendRequest(MyDelegate del)
{
storedDelegates.Add(del);
// Send request to server
}

public void CancelRequests(MyDelegate del)
{
Console.WriteLine("Cancel requests:");
int count = 0;
foreach (MyDelegate storedDel in storedDelegates)
{
count++;
Console.WriteLine("Delegate: " + count.ToString());
Console.WriteLine((del == storedDel).ToString());
Console.WriteLine(del.Equals(storedDel).ToString());
Console.WriteLine(MyDelegate.Equals(del,
storedDel).ToString());
Console.WriteLine(MyDelegate.ReferenceEquals(del,
storedDel).ToString());
}
// Send cancel to the server
}
}
}


Any thoughts on why that Invoke is getting in there?
 
G

Guest

See my comments below...
your code is a bit confusing. In one case, you're adding a delegate to the
list. In another, you're adding a method:


They are not the same. A delegate is a delegate; a method is a method.

Yes, I did this on purpose to demonstrate the different ways in which I have
tried to resolve the problem. When a parameter to a method is a delegate,
..NET allows me to either specify an explicit delegate OR a method matching
that delegate as the parameter, as I'm sure you're aware. It seems like
..NET 2.0 allows you to treat delegates and methods almost exactly the same.

I was just trying to test if I would get different results in my
comparisons, depending on which method I use (explicit delegate or method
name) to call SendRequest().
You have not posted your output, so I have no idea what the answer to the
following question is:

My output, I'm sure, would be the same as what you would get compiling and
running this code:
Cancel requests:
Delegate: 1
True
True
True
True
Delegate: 2
True
True
True
False
Cancel requests:
Delegate: 1
True
True
True
False
Delegate: 2
True
True
True
False

This is expected behavior, so my sample code isn't showing the problem that
I'm experiencing. It just illustrates the basic structure of the code I'm
debugging.
What Invoke getting in where?

Here's the scenario:
In my actual client code, I call the SendRequest() method a few times, each
time passing in the same delegate (or the same method name, when I tried it
that way), and then called the CancelRequests() method, passing in that same
delegate (or method name) -- exactly like I'm doing in my sample code above.

In VS, I put a break point in the CancelRequests() method, at the point
where the comparison between the passed-in delegate and the stored delegate
were compared. The equality comparison between these two was always
returning false, depsite the fact that I passed in the actual same delegate
to both the SendRequest() and CancelRequests() methods. This is what's
puzzling. I'm trying a few different things to get the proper comparison to
happen:

At compile-time, if I try to compare (using any of the equality comparisons
mentioned) the passed-in delegate with the stored delegate, I get the
following compile-time error:

The best overloaded method match for 'object.Equals(object, object)' has
some invalid arguments: Argument '1': cannot convert from 'method group' to
'object'

So, that makes sense. This is where the natural .NET facility to treat
methods and delegates the same breaks down. But this is in itself is
puzzling, because the the stored delegate reference is of the same type.
The reference inside the object is a delegate reference, so I don't
understand why .NET is now treating it as a method instead, when I'm doing
the comparison. The stored reference is in a hash table of request objects,
and inside each of those request objects is a delegate reference. That
reference is set to what was initialled passed in to SendRequest().

In an attempt to get around this problem, I create a new, temporary delegate
just before the comparison. The method I give as a parameter to the temp
delegate constructor is the stored delegate reference from inside the request
object.

When I inspect the two delegates at debug-time, here's what I see:

Stored delegate (direct reference to it from the hash table):
Type: my delegate type
Base: System.MulticastDelegate
Base: System.Delegate
Method: Void Callback(int x)
Target: null

Passed-in delegate:
Type: my delegate type
Base: System.MulticastDelegate
Base: System.Delegate
Method: Void Callback(int x)
Target: null

They look the same, as far as I can tell. But, remember, I am unable to
find a comparison method which will compare these directly and not give
compiler errors, since .NET is treating the stored delegate reference as a
'method group' and not a delegate!

So, as I explained, I wrapped up the stored delegate in a temporary delegate
and inspected it at debug time. This is what I get:
Type: my delegate type
Base: System.MulticastDelegate
Base: System.Delegate
Method: Void Callback(int x)
Target: Request object

And a comparison between this and the passed-in delegate fails (I presume)
because of the different targets.

So, in desperation, I ALSO wrapped the passed-in delegate in a seperate
temporary delegate. At debug-time it looked like this:
Type: my delegate type
Base: System.MulticastDelegate
Base: System.Delegate
Method: Void Invoke(int x)
Target: My delegate type

So, that's where the Invoke comes in. I assume then that me wrapping the
passed-in delegate in a temporary delegate, adds an extra delegate layer, and
isn't what I want.

Anyway,
Hopefully that better explains what is going wrong and maybe gives you an
idea of what I'm doing wrong.

The help is appreciated.
Thanks.
 
K

Kevin Spencer

I'm afraid I'm even *more* confused now. Sorry!

First, I can't tell what your input was for the output you posted. As you
explained, it's not the same as what you posted, but you weren't very
specific about matching up the various outputs to the actual code you *did*
use, which you haven't posted. Second, I can't tell what the difference is
between "my sample code" (which I am not sure I've seen yet), "my actual
code" (which I'm *sure* I have *not* seen yet), and when you refer to
"compile time" whether you're referring to your actual or your sample code,
and the difference between "compile time" and when "I put a break point in
the CancelRequests() method." I'm sure this is frustrating for you; I know
it's frustrating for me. And I'm not sure if it's just me, or if it's how
you're explaining it.

Perhaps it would help if, rather than jumping around, you organize your
information like so:

1. This is my sample code.
2. This is the output from this exact code.
3. This is my real code (with irrelevant code snipped)
4. This is the output when I run my real code exactly as I run my sample
code.
5. When I put a break point in my sample code, I observe this.
6. When I put a break point in my real code, I observe this.
7. My sample code throws no exceptions (and it performs correctly, or, what
it does incorrectly is...)
8. My real code throws this exception when I do this.
9. In order to handle the exception, I have tried this.
10. This is the result of what I tried.
11. - ? These are the other things I've tried, and what happened when I did.

I really want to help, but I need to be able to understand first!

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.
 
G

Guest

Well, the good news is that I've found the problem. As it turns out, I was
comparing the callback method in the request object with the passed-in
delegate in the CancelRequests() method. So, of course it would always fail!

However, while we're on the subject, perhaps you can elaborate on how .NET
can sometimes treat methods as delegates, and what's going on "under the
hood".

Consider the following code:

delegate void MyDelegate(int x);

class Foo
{
public void Request(MyDelegate del)
{
// ...
}
}

class Client
{
public void Callback(int x)
{
// ...
}

Foo foo = new Foo();
MyDelegate myDel = new MyDelegate(Callback);

public SendRequest()
{
foo.Request(myDel);
foo.Request(Callback);
}
}

In this example, calling Foo.Request() works both ways: passing in a
reference to myDel or the method name itself.

Is this an example of .NET 2.0's anonymous delegates? I haven't tried
compiling this code against 1.1. How does this work, and what's going on
under the hood?

To what extent can methods and delegates be treated the same, like this, and
what are the limits?
 
K

Kevin Spencer

Hi Quimbly,

Sometimes I also solve my own problems just by talking them through with
someone else. I think most creative people do. I could tell from your
questions that you're not the shabby type! I hope I at least helped you to
ask yourself the right questions.
Is this an example of .NET 2.0's anonymous delegates? I haven't tried
compiling this code against 1.1. How does this work, and what's going on
under the hood?

To what extent can methods and delegates be treated the same, like this,
and
what are the limits?

Here are a couple of SDK articles that explain this better than I could:

http://msdn2.microsoft.com/en-US/library/ms173171(VS.80).aspx
http://msdn2.microsoft.com/en-US/library/ms173174(VS.80).aspx

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.
 

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