【问题标题】:What is the equivalent of .NET events in Ruby?Ruby 中的 .NET 事件等价物是什么?
【发布时间】:2010-09-19 18:04:32
【问题描述】:

问题很简单。一个对象需要通知一些观察者可能感兴趣的事件。

当我坐下来验证我现在用 Ruby 编写的设计时,只是为了验证它。我发现自己对如何实现对象事件感到困惑。在 .Net 中,这将是单行的。.Net 还进行处理程序方法签名验证等。例如

// Object with events
public delegate void HandlerSignature(int a);
public event HandlerSignature MyEvent;
public event HandlerSignature AnotherCriticalEvent;

// Client
MyObject.MyEvent += new HandlerSignature(MyHandlerMethod); // MyHandlerMethod has same signature as delegate

是否有一个 EventDispatcher 模块或我缺少的东西可以绑在 Ruby 类上?希望得到一个符合 Ruby 的最小惊喜原则的答案。 事件是事件的名称加上事件发生时需要调用的 [observer, methodName] 对象队列。

【问题讨论】:

    标签: ruby events oop


    【解决方案1】:

    我已经创建了一个完全符合您要求的 gem,并且令人惊讶地称为 event_dispatcher,正如您所提到的。我希望它会帮助某人:event_dispatcher

    【讨论】:

      【解决方案2】:

      我是个菜鸟,但 Ruby 似乎真的很强大。您可以像这样自己实现 C# 样式事件:

      module Observable
          class Event 
              def initialize
                  @to_call = []
              end
              def fire(*arguments)
                  @to_call.each { |proc| proc.call(*arguments) }
              end
              def call(proc)
                  @to_call << proc
              end
              def dont_call(proc)
                  @to_call.delete proc
              end
          end
          def self.append_features(cls)
              def cls.event(sym)
                  define_method(sym.to_s) do
                      variable_name = "@#{sym}"
                      if not instance_variable_defined? variable_name then
                          instance_variable_set variable_name, Event.new
                      end
                      instance_variable_get variable_name
                  end
              end
          end
      end
      
      # Example 
      
      class Actor 
          include Observable
          event :whenActed
          def act
              whenActed.fire("Johnny") # fire event whenActed with parameter Johnny
          end
      end
      
      actor = Actor.new
      
      def apploud(whom)
          print "Bravo #{whom}!\n"
      end
      
      applouder = method(:apploud)
      
      actor.whenActed.call applouder
      
      actor.act
      

      【讨论】:

        【解决方案3】:

        查看各种ruby state machine libraries。他们打算解决一个大问题而不仅仅是事件,但可能会为您提供解决方案。

        我成功使用了state_machine gem,其中确实包括events

        【讨论】:

          【解决方案4】:

          对此有一个简短的说明。建议你看看EventMachine

          这是一个不同的外观相同的想法。它实现了一个事件驱动的范例,因此您比 .Net 事件的等价物高一级,并将EventMachine 模块视为 CLR 事件处理程序。

          同样退后一步,Ruby 遵循 Smalltalk 处理模型,其中对方法的任何调用都是发送给对象的消息(如事件一样)(请参阅 Send() 方法)。 EventMachine 为您提供事件的单线程切片。您可以使用 Rack 之类的东西来处理线程或工作线程。

          【讨论】:

            【解决方案5】:

            我为此编写了一个 gem,因为我遇到了完全相同的问题。试试这个:

            gem install ruby_events
            

            按照http://github.com/nathankleyn/ruby_events 上的说明进行操作,但简而言之:

            require 'rubygems'
            require 'ruby_events'
            
            class Example
              def initialize
                events.listen(:test_event) do |event_data|
                  puts 'Hai there!'
                  puts event_data
                end
              end
            
              def call_me
                events.fire(:test_event, 'My name is Mr Test Man!')
              end
            end
            
            e = Example.new
            e.call_me # Fires the event, and our handler gets called!
            

            【讨论】:

              【解决方案6】:

              为什么不编写自己的事件类?类似的东西

              class Event
                def initialize
                  @handlers = Array.new
                end
              
                def fire
                  @handlers.each do |v|
                    v.call
                  end
                end
              
                def << handler
                  @handlers << handler
                end
              end
              
              e = Event.new
              
              e << lambda { puts "hello" }
              e << lambda { puts "test" }
              e.fire
              

              这只是一个最小的示例,但可以通过任何方式进行扩展。在 .Net 中添加诸如 sender 和 eventArgs 之类的参数,或者您喜欢的任何参数 ;-)

              【讨论】:

                【解决方案7】:

                我要重申,在 Ruby 中没有语言级别的对 .NET 事件的模拟。 rails 处理它的方式是使用ActiveSupport::Callbacks(该页面上有一个示例)。

                【讨论】:

                • 如果我理解这一点..Rails 中的回调更像是钩子.. 您必须从一个类派生到某个点的插件行为。不完全是事件..它不会将“观察者”和“可观察者”结合起来
                【解决方案8】:

                首先,在 Ruby 中没有方法签名。最接近的是检查函数的数量。鸭子打字需要不同的思考(稍微)。

                Observable 模块是一个开始,但如果您需要从一个类中获取多个事件,那可能还不够。

                这是一个速写。它支持方法和块。根据需要进行修改以适应您的代码、线程方法等。例如,您可以使用 method_missing 在方法名称中包含事件名称,而不是将其作为参数。

                class EventBase
                    def initialize
                        @listeners = Hash.new
                    end
                
                    def listen_event(name, *func, &p)
                        if p
                            (@listeners[name] ||= Array.new) << p
                        else
                            (@listeners[name] ||= Array.new) << func[0]
                        end
                    end
                
                    def ignore_event(name, func)
                        return if !@listeners.has_key?(name)
                        @listeners[name].delete_if { |o| o == func }
                    end
                
                    def trigger_event(name, *args)
                        return if !@listeners.has_key?(name)
                        @listeners[name].each { |f| f.call(*args) }
                    end
                end
                
                
                class MyClass < EventBase
                    def raise_event1(*args)
                        trigger_event(:event1, *args)
                    end
                
                    def raise_event2(*args)
                        trigger_event(:event2, *args)
                    end
                end
                
                class TestListener
                    def initialize(source)
                        source.listen_event(:event1, method(:event1_arrival))
                        source.listen_event(:event2) do |*a|
                            puts "event 2 arrival, args #{a}"
                        end
                    end
                
                    def event1_arrival(*a)
                        puts "Event 1 arrived, args #{a}"
                    end
                end
                
                s = MyClass.new
                l = TestListener.new(s)
                
                s.raise_event1("here is event 1")
                s.raise_event2("here is event 2")
                

                【讨论】:

                  【解决方案9】:

                  【讨论】:

                  • 不完全......这似乎是观察者设计模式的实现。我需要的是让 Boiler 对象公开多个事件,例如 WaterExhausted、Started 等,以及使用他们的处理程序订阅的客户端(不限于单个“更新”方法)
                  猜你喜欢
                  • 2011-03-16
                  • 2010-10-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-23
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多