locked
Casting Generic Interface Method RRS feed

  • Question

  • Hi, Everybody!

    I am getting the following exception: Unable to cast object of type 'TestTimerEvents.ServiceNotifier' to type 'Foo.Interfaces.IWorkMethod'.

    TestTimerEvents.ServiceNotifier is defined as follows:

    namespace TestTimerEvents
    {
      public class ServiceNotifier : Foo.Interfaces.IWorkMethod
      {
        public void DoWork<T>(object sender, T e) where T : System.EventArgs
        {
          // Implementation...
        }
    
        // Other methods...
    
      }
    }
    
    

    Foo.Interfaces.IWorkMethod is defined as follows:

    namespace Foo.Interfaces
    {
      public interface IWorkMethod
      {
        void DoWork<T>(object sender, T e) where T : System.EventArgs;
      }
    }
    
    

    The exception occurs where indicated below… 

    
      var assembly = Assembly.LoadFile(wi.AssemblyFilePath);
      var instance = assembly.CreateInstance(wi.AssemblyType);
      ((IWorkMethod)instance).DoWork(wi.Sender, wi.EventArgs); // <-- HERE
     
    

    …where wi is a Foo.Threading.WorkItem.

    Foo.Threading.WorkItem is defined as follows:

    namespace Foo.Threading
    {
      class WorkItem
      {
        private string myAssemblyFilePath;
        private string myAssemblyType;
        private object mySender;
        private EventArgs myEventArgs;
    
        public WorkItem(string assemblyFilePath, string assemblyType, object sender, EventArgs e)
        {
          myAssemblyFilePath = assemblyFilePath;
          myAssemblyType = assemblyType;
          mySender = sender;
          myEventArgs = e;
        }
      }
    }
    
    

    At runtime, the actual type of e is System.Timers.ElapsedEventArgs.  The actual type of sender should not matter since everything derives from System.Object.

    So, why am I getting this exception?

     

    Saturday, December 18, 2010 10:48 AM

Answers

  • Hi
    I tried the code you posted and I can now repro the issue.
    Google revealed that it is a known problem.
    A solution is to declare the interface (IWorkMethod)
    in yet another assembly and reference this assembly
    to both the other assemblies (C#-projects)

    More details and another solution with csc-switches here:
    Plug-ins and cast exceptions

    Chris
    Saturday, December 18, 2010 11:04 PM

All replies

  • I am getting the following exception: Unable to cast object of type 'TestTimerEvents.ServiceNotifier' to type 'Foo.Interfaces.IWorkMethod'. TestTimerEvents.ServiceNotifier is defined as follows: var assembly = Assembly.LoadFile(wi.AssemblyFilePath); var instance = assembly.CreateInstance(wi.AssemblyType);
       ((IWorkMethod)instance).DoWork(wi.Sender, wi.EventArgs); //<-- HERE

     

    You didn't post the declaration of the two props 'Sender' and
    'EventArgs' of class WorkItem - only the corresponding fields with
    the my-Prefixes are shown.
    An easy (maybe too easy) explanation could be, that the prop named
    'EventArgs' of class WorkItem isn't actually of type 'System.EventArgs'
    or that its value is null at runtime.
    So the evaluation of type T and its constraint would fail at runtime
    which would also cause the cast to fail.

    Chris

    Saturday, December 18, 2010 12:39 PM
  • You are not using the var keyword properly.  Please don't tell me what it does.  I know what it does.  The type is inferred at compile time.  Get rid of it in this scenario altogether.  I assume that you have the interface defined in only one assembly.  Your code is not providing information about the type until run time, late binding.  

    http://msdn.microsoft.com/en-us/library/bb383973.aspx

    The var keyword is probably being defined as System.Object by the C# compiler.  Have you tried moving the cast to the Interface to the CreateInstance line of code, and removing the var keyword by replacing it with the Interface?

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Saturday, December 18, 2010 7:19 PM
    Moderator
  • I just noticed something.  Can you post the code for the method CreateInstance, or a link in the MSDN library.  Please identify the Class that defines it, too.  Assembly?

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Saturday, December 18, 2010 7:48 PM
    Moderator
  • I don't get any error. Check the actual type of instance.
    Saturday, December 18, 2010 11:03 PM
  • Hi
    I tried the code you posted and I can now repro the issue.
    Google revealed that it is a known problem.
    A solution is to declare the interface (IWorkMethod)
    in yet another assembly and reference this assembly
    to both the other assemblies (C#-projects)

    More details and another solution with csc-switches here:
    Plug-ins and cast exceptions

    Chris
    Saturday, December 18, 2010 11:04 PM
  • Since assembly is the return value of Assembly.LoadFile, its type cannot be anything else than Assembly. Unless the OP has defined an Assembly class, in which case LoadFile could return anything.
    Saturday, December 18, 2010 11:05 PM
  • The InvalidCastException is thrown if the target Type (IWorkMethod)
    is defined in the dynamically loaded assembly and this assembly
    is also referenced by the C#-project -
    which it has to be the case to make the cast compile.

    There's no exception if all types are of the same assembly
    or the target type is declared in yet a third assembly.

     

    Chris

    Saturday, December 18, 2010 11:33 PM
  • I assume that the type is Assembly. 

    =============================

    I think the problem is either different definitions of the interface, as I noted earlier, in multiple assemblies.  Or it could simply be the mis-use of the 'var' keyword.  

    Yeah, I know it is supposed to work.  I just don't trust Compilers to do work for me that I easily can do myself.  I have seen countless posts where the type is known but the keyword 'var' is used anyway.  Removing the keywords and actually specifying the class fixes the problem more times than not.  If you know the type, then there is absolutely no reason to use "var'.

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Saturday, December 18, 2010 11:35 PM
    Moderator
  • The InvalidCastException is thrown if the target Type (IWorkMethod)
    is defined in the dynamically loaded assembly and this assembly
    is also referenced by the C#-project -
    which it has to be the case to make the cast compile.

    There's no exception if all types are of the same assembly
    or the target type is declared in yet a third assembly.

     

    Chris


    If you ask the CLR to load an assembly that is already loaded, it is smart enough to use the version that it already loaded.  The CLR knows where each and every loaded assembly was loaded from.  If you ask it to load the same assembly from a different location, the CLR assumes it is a different assembly with different types.

    BTW.  Visual Studio also uses its' own copies of Framework assemblies to compile the source code.  Different copies in the Windows directory used by the CLR when it runs the code.

    C:\Program Files\Reference Assemblies\Microsoft\Framework\

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Saturday, December 18, 2010 11:50 PM
    Moderator
  • Hi, Christoph!

    Thank-you for your response...

    Excellent observation!  However, in order to keep the post short, I trimmed some details.

    Please see the full WorkItem definition (with properties).

    namespace Foo.Threading
    {
      class WorkItem
      {
        private string myAssemblyFilePath;
        private string myAssemblyType;
        private object mySender;
        private EventArgs myEventArgs;
    
        public string AssemblyFilePath { get { return myAssemblyFilePath; } }
        public string AssemblyType   { get { return myAssemblyType; } }
        public object Sender      { get { return mySender; } }
        public EventArgs EventArgs   { get { return myEventArgs; } }
    
        public WorkItem(string assemblyFilePath, string assemblyType, object sender, EventArgs e)
        {
          myAssemblyFilePath = assemblyFilePath;
          myAssemblyType = assemblyType;
          mySender = sender;
          myEventArgs = e;
        }
      }
    }
    
    Monday, December 20, 2010 6:54 AM
  • P.S.  The values of wi.Sender, wi.EventArgs are not null, and wi.EventArgs is derived from System.EventArgs, specifically System.IO.FileSystemEventArgs, System.IO.RenamedEventArgs and System.Timers.ElapsedEventArgs.

    Again, thank-you for your suggestions!

    Monday, December 20, 2010 7:05 AM
  • Hey, RudeDog2!

    I replaced all vars with explicit types and moved the cast to the CreateInstance line of code. 

    
      Assembly assembly = Assembly.LoadFile(wi.AssemblyFilePath);
      IWorkMethod instance = (IWorkMethod)assembly.CreateInstance(wi.AssemblyType); // <-- HERE
      instance.DoWork(wi.Sender, wi.EventArgs);
    
    
    

    Same exception...  one line sooner!   :-P

    Monday, December 20, 2010 7:56 AM
  • BTW...  You assumed incorrectly: The interface was defined in all assemblies!  :-P
    Monday, December 20, 2010 8:02 AM
  • Hey, RudeDog2!

    As per your request: Link to Assembly.CreateInstance Method (.NET Framework 3.5) in MSDN Library.

    :-P

    Monday, December 20, 2010 8:12 AM
  • BTW...  You assumed incorrectly: The interface was defined in all assemblies!  :-P

    Do you mean the loaded assembly has its own definition of the IWorkMethod interface? In that case, it's not the same interface as the one you try to cast to.
    Monday, December 20, 2010 9:49 AM
  • Hi, Louis.fr!

    Sorry...  The interface is identical in each assembly.

    I truly appreciate your help!

    Thank-you!

    Monday, December 20, 2010 1:39 PM
  • Am 20.12.2010 14:39, schrieb Informatosaurus:

    Hi, Louis.fr!

    Sorry...  The interface is identical in each assembly.

    I truly appreciate your help!

    Hi

    do you declare the interface in each assembly, i.e.
    do you have a IWorkMethod.cs file (or another *.cs-file)
    that contains:

    namespace Foo.Interfaces
    {
      public interface IWorkMethod
      {
        void DoWork<T>(object sender, T e) where T : System.EventArgs;
      }
    }

    in all these assemblies?
    Then these are still different types.

    In order to have only one type you must declare in
    only one assembly and let the other assemblies
    refrence it (Project\Referencec\Add).

    Chris

    Monday, December 20, 2010 1:47 PM
  • Hi, Chris!

    You are absolutely correct...

    In fact, when I started this project, I originally had each component in separate DLLs (i.e., main, interfaces, timers, file sytem watchers and separate work/tasks), each project referencing the necessary DLLs.  Everything worked perfectly!

    Little did I know that by trying to "consolidate" the projects, I would end up breaking it!

    Thank-you, very much!

    Your answer is the correct one!

    Monday, December 20, 2010 3:22 PM
  • BTW...  You assumed incorrectly: The interface was defined in all assemblies!  :-P


    My mistake.  I made a false assumption in my initial post on this thread.  Happy Coding.

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Monday, December 20, 2010 10:51 PM
    Moderator
  • Hey, Rudedog2!

    No hard feelings...

    BTW...  The code works perfectly with the vars...  ;-P

    Tuesday, December 21, 2010 7:38 AM