我如何可以订阅跨应用程序域事件(object.Event + =处理程序;) [英] How can I subscribe to an event across AppDomains (object.Event += handler;)

查看:117
本文介绍了我如何可以订阅跨应用程序域事件(object.Event + =处理程序;)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有在这个留言板帖子描述的问题。

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

 公共类的MyObject:MarshalByRefObject的
{
    公共事件的EventHandler TheEvent;
    ...
    ...
}
 

我想添加一个处理该事件。该处理器将在不同的AppDomain中运行。我的理解是,这是所有好的,事件被传递跨越了边界的神奇,用.NET Remoting的。

但是,当我做到这一点:

  //实例是一个对象是运行在一个单独的AppDomain实例
instance.TheEvent + = this.Handler;
 

...它编译罚款,但未能在运行时:

  System.Runtime.Remoting.RemotingException:
     远程找不到场TheEvent在类型为MyObject。
 

为什么呢?

修改:源$ C ​​$工作附录C演示该问题:

  // EventAcrossAppDomain.cs
// ------------------------------------------------ ------------------
//
//展示欲以跨应用程序域事件时发生异常。
//
//唯一的例外是:
// System.Runtime.Remoting.RemotingException:
//远程找不到场TimerExpired上键入Cheeso.Tests.EventAcrossAppDomain.MyObject。
//
//编译:
// C:\在.net3.5 \ csc.exe的/ T:EXE /调试:全/out:EventAcrossAppDomain.exe EventAcrossAppDomain.cs
//

使用系统;
使用的System.Threading;
使用的System.Reflection;

命名空间Cheeso.Tests.EventAcrossAppDomain
{
    公共类为MyObject:MarshalByRefObject的
    {
        公共事件的EventHandler TimerExpired;
        公共事件处理程序TimerExpired2;

        公众为MyObject(){}

        公共无效围棋(INT秒)
        {
            _timeToSleep =秒;
            ThreadPool.QueueUserWorkItem(延迟);
        }

        私人无效延迟(对象将stateInfo)
        {
            System.Threading.Thread.Sleep(_timeToSleep * 1000);
            OnExpiration();
        }

        私人无效OnExpiration()
        {
            Console.WriteLine(OnExpiration(主题ID = {0}),
                              Thread.CurrentThread.ManagedThreadId);
            如果(TimerExpired!= NULL)
                TimerExpired(这一点,EventArgs.Empty);

            如果(TimerExpired2!= NULL)
                TimerExpired2(这一点,EventArgs.Empty);
        }

        私人无效ChildObjectTimerExpired(对象源,System.EventArgs发送)
        {
            Console.WriteLine(ChildObjectTimerExpired(主题ID = {0}),
                              Thread.CurrentThread.ManagedThreadId);
            _foreignObjectTimerExpired.Set();
        }

        公共无效运行(布尔demonstrateProblem)
        {
            尝试
            {
                Console.WriteLine(\ nRun()...({0}),
                                  (demonstrateProblem)
                                  ? 将展示问题
                                  :将避免的问题);

                INT delaySeconds = 4;
                AppDomain中的AppDomain = AppDomain.CreateDomain(appDomain2);
                字符串exeAssembly = Assembly.GetEntryAssembly()全名。

                为MyObject O =(MyObject来)appDomain.CreateInstanceAndUnwrap(exeAssembly,
                                                                          typeof运算(为MyObject).FullName);

                如果(demonstrateProblem)
                {
                    //这里发生了异常
                    o.TimerExpired + = ChildObjectTimerExpired;
                }
                其他
                {
                    //解决方法:不使用活动
                    o.TimerExpired2 = ChildObjectTimerExpired;
                }

                _foreignObjectTimerExpired =新的ManualResetEvent(假);

                o.Go(delaySeconds);

                Console.WriteLine(运行():托管对象将等待{0}秒...(主题ID = {1}),
                                  delaySeconds,
                                  Thread.CurrentThread.ManagedThreadId);

                _foreignObjectTimerExpired.WaitOne();

                Console.WriteLine(运行():完成。);

            }
            赶上(System.Exception的EXC1)
            {
                Console.WriteLine(在run(),\ N {0},exc1.ToString());
            }
        }



        公共静态无效的主要(字串[] args)
        {
            尝试
            {
                变种O =新的MyObject();
                o.Run(真正的);
                o.Run(假);
            }
            赶上(System.Exception的EXC1)
            {
                Console.WriteLine(在main(),\ N {0},exc1.ToString());
            }
        }

        //私人领域
        私人诠释_timeToSleep;
        私人ManualResetEvent的_foreignObjectTimerExpired;

    }
}
 

解决方案

这是你的code例如失败的原因是,事件声明和code表示赞同它是在同一类。

在这种情况下,编译器优化的code。通过使code表示赞同基础字段直接的事件访问。

基本上,而不是做这个(为类之外的任何code必须):

  o.add_Event(delegateInstance);
 

它这样做:

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

所以,这个问题我对你是这样的:你的真实的例子有code相同的布局?是code表示赞同该声明的情况下在同一个类中的事件?

如果是,那么接下来的问题是:它必须在那里,还是应该真正被感动出来的吗?通过移动code OUT之类的,你让编译器使用添加和? 删除已添加到您的对象特殊方法。

另一种方法,如果你不能或不动code,将接手负责添加和删除代表您的活动:

 私人事件处理程序_TimerExpired;
公共事件的EventHandler TimerExpired
{
    加
    {
        _TimerExpired + =价值;
    }

    去掉
    {
        _TimerExpired  -  =价值;
    }
}
 

这迫使编译器调用add,甚至从code删除相同的类中。

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

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

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

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.

But, when I do this:

// 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'.

Why?

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.

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

o.add_Event(delegateInstance);

it does this:

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?

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;
    }
}

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

这篇关于我如何可以订阅跨应用程序域事件(object.Event + =处理程序;)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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