tags:

views:

153

answers:

1

We have a web service framework we use; which I had no part in creating and have no ability to modify, that requires, essentially, cookie-cutter code to add a "module" to the framework. A sample of the format of the code is below.

In the example below, I added the Helper class to each of the classes below several months apart without realizing I had two classes named the same. The code compiles fine, but when deployed to the web servers it produces a run-time error. Whenever either of the Processor_ProcessCompleted event handlers get hits, it throws an invalid cast exception stating that an object of type Helper cannot be cast to type Helper.

I understand the error, and have since remedied the situation by re-factoring the Helper class out. These helper classes are never referenced anywhere outside their own class, they are private child classes, they are in different namespaces, and neither of the namespaces, in any way, references the other namespace. My question is: why is this collision occurring?

Obviously, I am incorrect, but I thought the mere act of declaring them private and internal (not to be confused with internal) to their parent classes would have insulated them from all possibility of collision.


namespace NameSpace1
{
  public class Client1
  {
    Processor processor;

    private class Helper
    {
      public Property1 { get; set; }
      public Property2 { get; set; }
    }

    public Client1(Processor p)
    {
      processor = p;
      processor.ProcessCompleted += Processor_ProcessCompleted;
    }

    void Processor_Process(object sender, ProcessCompletedEventArgs e)
    {
      Helper helper = (Helper)e.UserState;
      // ... do a bunch of stuff
    }
  }
}

namespace NameSpace2
{
  public class Client2
  {
    Processor processor;

    private class Helper
    {
      public Property1 { get; set; }    
    }

    public Client2(Processor p)
    {
      processor = p;
      processor.ProcessCompleted += Processor_ProcessCompleted;
    }

    void Processor_Process(object sender, ProcessCompletedEventArgs e)
    {
      Helper helper = (Helper)e.UserState;
      // ... do a bunch of stuff
    }
  }
}

Re Jon Skeet: Jon, thanks for the reply. In this case, all the clients use the same processor. All of them assign their event handler to the event. I understand what you are saying, but since the cast occurs in the context of the specific method, it seems as though it should be able to infer the specific type from the context of the method.

As for the assignemnt of the helper, the processor itself never actually looks at what it it stuffing into the EventArgs. It's just an object to the processor. The asynch call looks like:


    processor.Process(request, helper);

and it occurrs inside the same class that is attempting to cast it at the response. The helper just contains some simple data that the service uses to determine path of execution.

Edit to address Jon's Edit: Jon, I now think I see the problem. The designer of the framework specifically designed it to allow all the clients to use the same processor. Thus I think you are perfectly correct about the events being fired for all subscribed clients. In fact, the person using the client is writing code like this:


Processor processor = new Processor();
Client1 client1 = new Client1(processor);
Client2 client2 = new Client2(processor);

client1.ExecuteSomeRequest(); // calls processor.Process(request, helper);
client2.ExecuteSomeOtherRequest(); // calls processor.Process(request, helper);

I appreciate the diligent and comprehensive answer.

+3  A: 

No, there's still a distinct possibility of collision - because both clients can end up adding an event handler to the same Processor, at least with the code you've provided. Both will then try to cast e.UserState to Helper and only one of them will be right. What's meant to be setting the UserState, and how is it meant to know which kind of Helper to set it to? Are you sure you never pass the same Process to more than one Client?

EDIT: Responding to your edit - if all the clients are using the same processor, then surely they'll all be subscribed to the same event, which probably isn't what you want.

As for the bit about the UserState - you've explained that it's called as processor.Process(request, helper); but who's making that call, and what's the exact type of helper?

I suspect the problem is due to you reusing the same processor object, so previous clients are still having their event handler fired even with a new client, but it's hard to know without the complete picture.

If you could produce a short but complete program demonstrating the problem, that would help.

Jon Skeet
Thanks for the info, Jon. I updated the post to supply a little more info.
John Kraft