|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Comparing delegatescreate, 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. 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. -- Show quoteHTH, Kevin Spencer Microsoft MVP Professional Numbskull Hard work is a medication for which there is no placebo. "Quimbly" <Quim***@discussions.microsoft.com> wrote in message news:CE6D2255-1CF6-43EE-A514-5AE625B49A43@microsoft.com... > 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. 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. 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? On Thu, 20 Apr 2006 16:05:02 -0700, Quimbly
<Quim***@discussions.microsoft.com> wrote: > Here's the basic structure. Note, however, I don't have a problem with this I think you're going to have to go through the painful process of> 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. chopping back the code while the problem is still there, until you have a simple repro which *does* demonstrate the problem. -- Barry 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); They are not the same. A delegate is a delegate; a method is a method.> connection.SendRequest(Callback); 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?-- Show quoteHTH, Kevin Spencer Microsoft MVP Professional Numbskull Hard work is a medication for which there is no placebo. "Quimbly" <Quim***@discussions.microsoft.com> wrote in message news:376294A8-1545-43D8-81EA-CD69CA969989@microsoft.com... > 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? See my comments below...
> your code is a bit confusing. In one case, you're adding a delegate to the Yes, I did this on purpose to demonstrate the different ways in which I have > 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. 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 My output, I'm sure, would be the same as what you would get compiling and > following question is: 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. > > Any thoughts on why that Invoke is getting in there? Here's the scenario:> > What Invoke getting in where? 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. 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! -- Show quoteHTH, Kevin Spencer Microsoft MVP Professional Numbskull Hard work is a medication for which there is no placebo. "Quimbly" <Quim***@discussions.microsoft.com> wrote in message news:B218FF4E-3FC4-400B-8F98-524CCC362A55@microsoft.com... > 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: >> >> > connection.SendRequest(myDel); >> > connection.SendRequest(Callback); >> >> 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. > >> > Any thoughts on why that Invoke is getting in there? >> >> 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. 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? 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 Here are a couple of SDK articles that explain this better than I could:> 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? http://msdn2.microsoft.com/en-US/library/ms173171(VS.80).aspx http://msdn2.microsoft.com/en-US/library/ms173174(VS.80).aspx -- Show quoteHTH, Kevin Spencer Microsoft MVP Professional Numbskull Hard work is a medication for which there is no placebo. "Quimbly" <Quim***@discussions.microsoft.com> wrote in message news:A62A156A-1C60-4A62-BFB5-035441FA76A3@microsoft.com... > 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? > > |
|||||||||||||||||||||||