如何在AppDomains(object.Event + = handler;)之间订阅一个事件? [英] How can I subscribe to an event across AppDomains (object.Event += handler;)

查看:188
本文介绍了如何在AppDomains(object.Event + = handler;)之间订阅一个事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了此留言板帖子中描述的问题。

I'm having the problem described in this message board post.

我有一个托管在其自己的AppDomain中的对象。

I have an object that is hosted in its own AppDomain.

public class MyObject : MarshalByRefObject
{
    public event EventHandler TheEvent;
    ...
    ...
}

d喜欢为该事件添加一个处理程序。处理程序将在不同的AppDomain中运行。我的理解是这一切都很好,事件通过.NET Remoting神奇地传递到这个边界。

I'd like to add a handler to that event. The handler will run in a different AppDomain. My understanding is this is all good, events get delivered across that boundary magically, with .NET Remoting.

但是,当我这样做:

// instance is an instance of an object that runs in a separate AppDomain
instance.TheEvent += this.Handler ; 

...它编译正常,但在运行时失败:

...it compiles fine but fails at runtime with:

System.Runtime.Remoting.RemotingException: 
     Remoting cannot find field 'TheEvent' on type 'MyObject'.

为什么?

编辑:演示问题的工作应用程序的源代码:

EDIT: source code of working app that demonstrates the problem:

// EventAcrossAppDomain.cs
// ------------------------------------------------------------------
//
// demonstrate an exception that occurs when trying to use events across AppDomains.
//
// The exception is:
// System.Runtime.Remoting.RemotingException:
//       Remoting cannot find field 'TimerExpired' on type 'Cheeso.Tests.EventAcrossAppDomain.MyObject'.
//
// compile with:
//      c:\.net3.5\csc.exe /t:exe /debug:full /out:EventAcrossAppDomain.exe EventAcrossAppDomain.cs
//

using System;
using System.Threading;
using System.Reflection;

namespace Cheeso.Tests.EventAcrossAppDomain
{
    public class MyObject : MarshalByRefObject
    {
        public event EventHandler TimerExpired;
        public EventHandler TimerExpired2;

        public  MyObject() { }

        public void Go(int seconds)
        {
            _timeToSleep = seconds;
            ThreadPool.QueueUserWorkItem(Delay);
        }

        private void Delay(Object stateInfo)
        {
            System.Threading.Thread.Sleep(_timeToSleep * 1000);
            OnExpiration();
        }

        private void OnExpiration()
        {
            Console.WriteLine("OnExpiration (threadid={0})",
                              Thread.CurrentThread.ManagedThreadId);
            if (TimerExpired!=null)
                TimerExpired(this, EventArgs.Empty);

            if (TimerExpired2!=null)
                TimerExpired2(this, EventArgs.Empty);
        }

        private void ChildObjectTimerExpired(Object source, System.EventArgs e)
        {
            Console.WriteLine("ChildObjectTimerExpired (threadid={0})",
                              Thread.CurrentThread.ManagedThreadId);
            _foreignObjectTimerExpired.Set();
        }

        public void Run(bool demonstrateProblem)
        {
            try 
            {
                Console.WriteLine("\nRun()...({0})",
                                  (demonstrateProblem)
                                  ? "will demonstrate the problem"
                                  : "will avoid the problem");

                int delaySeconds = 4;
                AppDomain appDomain = AppDomain.CreateDomain("appDomain2");
                string exeAssembly = Assembly.GetEntryAssembly().FullName;

                MyObject o = (MyObject) appDomain.CreateInstanceAndUnwrap(exeAssembly,
                                                                          typeof(MyObject).FullName);

                if (demonstrateProblem)
                {
                    // the exception occurs HERE
                    o.TimerExpired += ChildObjectTimerExpired;
                }
                else
                {
                    // workaround: don't use an event
                    o.TimerExpired2 = ChildObjectTimerExpired;
                }

                _foreignObjectTimerExpired = new ManualResetEvent(false);

                o.Go(delaySeconds);

                Console.WriteLine("Run(): hosted object will Wait {0} seconds...(threadid={1})",
                                  delaySeconds,
                                  Thread.CurrentThread.ManagedThreadId);

                _foreignObjectTimerExpired.WaitOne();

                Console.WriteLine("Run(): Done.");

            }
            catch (System.Exception exc1)
            {
                Console.WriteLine("In Run(),\n{0}", exc1.ToString());
            }
        }



        public static void Main(string[] args)
        {
            try 
            {
                var o = new MyObject();
                o.Run(true);
                o.Run(false);
            }
            catch (System.Exception exc1)
            {
                Console.WriteLine("In Main(),\n{0}", exc1.ToString());
            }
        }

        // private fields
        private int _timeToSleep;
        private ManualResetEvent _foreignObjectTimerExpired;

    }
}


推荐答案

您的代码示例失败的原因是事件声明和订阅的代码在同一个类中。

The reason that your code example fails is that the event declaration and the code that subscribes to it is in the same class.

在这种情况下,编译器通过使订阅事件的代码直接访问基础字段来优化代码。

In this case, the compiler "optimizes" the code by making the code that subscribes to the event access the underlying field directly.

基本上,不是这样做(因为类之外的任何代码都将to):

Basically, instead of doing this (as any code outside of the class will have to):

o.add_Event(delegateInstance);

这样做:

o.EventField = (DelegateType)Delegate.Combine(o.EventField, delegateInstance);

所以,我给你的问题是这样的:你的真实例子是否具有相同的代码布局?是否在同一个类中订阅事件的代码声明事件?

so, the question I have for you is this: Does your real example have the same layout of code? Is the code that subscribes to the event in the same class that declares the event?

如果是,则下一个问题是:是否必须在那里,或应该它真的被搬出来了吗?通过将代码移出类,您可以使编译器使用 add 和? 删除添加到对象中的特殊方法。

If yes, then the next question is: Does it have to be there, or should it really be moved out of it? By moving the code out of the class, you make the compiler use the add and ? remove special methods that are added to your object.

另一种方式,如果你不能或不会移动代码,将负责添加和删除您的活动的代理人:

The other way, if you cannot or won't move the code, would be to take over responsibility for adding and removing delegates to your event:

private EventHandler _TimerExpired;
public event EventHandler TimerExpired
{
    add
    {
        _TimerExpired += value;
    }

    remove
    {
        _TimerExpired -= value;
    }
}

这将强制编译器调用add和remove从同一课程内的代码。

This forces the compiler to call the add and remove even from code inside the same class.

这篇关于如何在AppDomains(object.Event + = handler;)之间订阅一个事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆