Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Problems with Rx EventMessager [Solved]

Problems with Rx EventMessager [Solved]

Scheduled Pinned Locked Moved C#
question
5 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Kenneth Haugland
    wrote on last edited by
    #1

    Im trying to publish a message that should communicate between classes:

    public sealed class EventMessager : IEventMessager
    {
        private readonly Dictionary subscriberLookup = new Dictionary();
    
        public IObservable Observe()
        {
            if (!subscriberLookup.TryGetValue(typeof(T), out object subject))
            {
                subject = new Subject();
                subscriberLookup.Add(typeof(T), subject);
            }
            return ((ISubject)subject).AsObservable();
        }
    
        public void Publish(T @event)
        {
            if (subscriberLookup.TryGetValue(@event.GetType(), out object subject))
            {
                ((Subject)subject).OnNext(@event);
            }
        }
    }
    

    This works well, except for the time I wanted to use Reflection in order to initate a new Message class like so:

    eventMessager.Publish(Activator.CreateInstance(w[0]));

    It complains that it cannot cast the subject of class type that w is as an Subject Object. It will work as expected if I just say new MyClass instead of the activator. I really cant find any solutions online either, so does anyone know what is going on here?

    P Richard DeemingR 2 Replies Last reply
    0
    • K Kenneth Haugland

      Im trying to publish a message that should communicate between classes:

      public sealed class EventMessager : IEventMessager
      {
          private readonly Dictionary subscriberLookup = new Dictionary();
      
          public IObservable Observe()
          {
              if (!subscriberLookup.TryGetValue(typeof(T), out object subject))
              {
                  subject = new Subject();
                  subscriberLookup.Add(typeof(T), subject);
              }
              return ((ISubject)subject).AsObservable();
          }
      
          public void Publish(T @event)
          {
              if (subscriberLookup.TryGetValue(@event.GetType(), out object subject))
              {
                  ((Subject)subject).OnNext(@event);
              }
          }
      }
      

      This works well, except for the time I wanted to use Reflection in order to initate a new Message class like so:

      eventMessager.Publish(Activator.CreateInstance(w[0]));

      It complains that it cannot cast the subject of class type that w is as an Subject Object. It will work as expected if I just say new MyClass instead of the activator. I really cant find any solutions online either, so does anyone know what is going on here?

      P Offline
      P Offline
      Pete OHanlon
      wrote on last edited by
      #2

      I'm a bit tired here so I'm having a problem groking what you want to achieve here but one thing that stands out is that you are calling a generic Publish method with a reflected object and expecting Subject to know what type the subject is.

      This space for rent

      1 Reply Last reply
      0
      • K Kenneth Haugland

        Im trying to publish a message that should communicate between classes:

        public sealed class EventMessager : IEventMessager
        {
            private readonly Dictionary subscriberLookup = new Dictionary();
        
            public IObservable Observe()
            {
                if (!subscriberLookup.TryGetValue(typeof(T), out object subject))
                {
                    subject = new Subject();
                    subscriberLookup.Add(typeof(T), subject);
                }
                return ((ISubject)subject).AsObservable();
            }
        
            public void Publish(T @event)
            {
                if (subscriberLookup.TryGetValue(@event.GetType(), out object subject))
                {
                    ((Subject)subject).OnNext(@event);
                }
            }
        }
        

        This works well, except for the time I wanted to use Reflection in order to initate a new Message class like so:

        eventMessager.Publish(Activator.CreateInstance(w[0]));

        It complains that it cannot cast the subject of class type that w is as an Subject Object. It will work as expected if I just say new MyClass instead of the activator. I really cant find any solutions online either, so does anyone know what is going on here?

        Richard DeemingR Offline
        Richard DeemingR Offline
        Richard Deeming
        wrote on last edited by
        #3

        Activator.CreateInstance(Type) returns an Object. Activator.CreateInstance Method (System) | Microsoft Docs[^] As a result, you are calling eventMessager.Publish<object>(instanceOfYourType). That uses the run-time type of the parameter to find the subscriber. This will be a Subject<YourClass> instance. You then try to cast that instance to Subject<object>, which fails because the Subject<T> class is not covariant on its type parameter. Covariance and Contravariance in Generics | Microsoft Docs[^] You can't even get away with casting the subscriber to the IObserver<T> interface, since that interface is contravariant. You're going to need to use reflection to invoke the method. I'd suggest using a separate method to avoid making the regular method slower:

        public void PublishNew(Type eventType)
        {
        if (subscriberLookup.TryGetValue(eventType, out object subject))
        {
        object @event = Activator.CreateInstance(eventType);
        Type t = typeof(IObserver<>).MakeGenericType(eventType);
        MethodInfo onNext = t.GetMethod("OnNext");
        onNext.Invoke(subject, new[] { @event });
        }
        }

        ...

        eventMessager.PublishNew(w[0]);


        "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

        "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

        K 2 Replies Last reply
        0
        • Richard DeemingR Richard Deeming

          Activator.CreateInstance(Type) returns an Object. Activator.CreateInstance Method (System) | Microsoft Docs[^] As a result, you are calling eventMessager.Publish<object>(instanceOfYourType). That uses the run-time type of the parameter to find the subscriber. This will be a Subject<YourClass> instance. You then try to cast that instance to Subject<object>, which fails because the Subject<T> class is not covariant on its type parameter. Covariance and Contravariance in Generics | Microsoft Docs[^] You can't even get away with casting the subscriber to the IObserver<T> interface, since that interface is contravariant. You're going to need to use reflection to invoke the method. I'd suggest using a separate method to avoid making the regular method slower:

          public void PublishNew(Type eventType)
          {
          if (subscriberLookup.TryGetValue(eventType, out object subject))
          {
          object @event = Activator.CreateInstance(eventType);
          Type t = typeof(IObserver<>).MakeGenericType(eventType);
          MethodInfo onNext = t.GetMethod("OnNext");
          onNext.Invoke(subject, new[] { @event });
          }
          }

          ...

          eventMessager.PublishNew(w[0]);


          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

          K Offline
          K Offline
          Kenneth Haugland
          wrote on last edited by
          #4

          Ah, that works perfectly, thank you.

          1 Reply Last reply
          0
          • Richard DeemingR Richard Deeming

            Activator.CreateInstance(Type) returns an Object. Activator.CreateInstance Method (System) | Microsoft Docs[^] As a result, you are calling eventMessager.Publish<object>(instanceOfYourType). That uses the run-time type of the parameter to find the subscriber. This will be a Subject<YourClass> instance. You then try to cast that instance to Subject<object>, which fails because the Subject<T> class is not covariant on its type parameter. Covariance and Contravariance in Generics | Microsoft Docs[^] You can't even get away with casting the subscriber to the IObserver<T> interface, since that interface is contravariant. You're going to need to use reflection to invoke the method. I'd suggest using a separate method to avoid making the regular method slower:

            public void PublishNew(Type eventType)
            {
            if (subscriberLookup.TryGetValue(eventType, out object subject))
            {
            object @event = Activator.CreateInstance(eventType);
            Type t = typeof(IObserver<>).MakeGenericType(eventType);
            MethodInfo onNext = t.GetMethod("OnNext");
            onNext.Invoke(subject, new[] { @event });
            }
            }

            ...

            eventMessager.PublishNew(w[0]);


            "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

            K Offline
            K Offline
            Kenneth Haugland
            wrote on last edited by
            #5

            Richard Deeming wrote:

            I'd suggest using a separate method to avoid making the regular method slower:

            I found some other lower level code that the author claimed was 10 times faster than Activator.CreateInastance:

            public class ObjectCreateMethod
            {
            delegate object MethodInvoker();
            MethodInvoker methodHandler = null;

                public ObjectCreateMethod(Type type)
                {
                    CreateMethod(type.GetConstructor(Type.EmptyTypes));
                }
            
                public ObjectCreateMethod(ConstructorInfo target)
                {
                    CreateMethod(target);
                }
            
                void CreateMethod(ConstructorInfo target)
                {
                    DynamicMethod dynamic = new DynamicMethod(string.Empty,
                                typeof(object),
                                new Type\[0\],
                                target.DeclaringType);
                    ILGenerator il = dynamic.GetILGenerator();
                    il.DeclareLocal(target.DeclaringType);
                    il.Emit(OpCodes.Newobj, target);
                    il.Emit(OpCodes.Stloc\_0);
                    il.Emit(OpCodes.Ldloc\_0);
                    il.Emit(OpCodes.Ret);
            
                    methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
                }
            
                public object CreateInstance()
                {
                    return methodHandler();
                }
            }
            

            But you have solved my problem, so this does not bother me as much.

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • World
            • Users
            • Groups