我有一个使用CreateInstanceAndUnwrap在AppDomain中创建的类。该类包含System.Threading.Timer。
我遇到的问题是,当实例化类时,计时器的回调方法似乎看不到类实例的正确值。
我在下面有示例代码说明问题:
图书馆类
using System;
using System.Threading;
namespace Library
{
[Serializable]
public class Class1
{
public Class1()
{
Started = false;
_Timer = new Timer(TimerMethod);
}
public bool Started { get; set; }
private readonly Timer _Timer;
private string _Message;
private string _TimerMessage;
public bool Start()
{
Started = true;
_Message = string.Format("Class1 says Started = {0}", Started);
_TimerMessage = "Timer message not set yet";
_Timer.Change(1000, 1000);
return Started;
}
public string GetMessage()
{
// _TimerMessage is never set by TimerMethod when this class is created within an AppDomain
return string.Format("{0}, {1}", _Message, _TimerMessage);
}
public void TimerMethod(object state)
{
// Started is always false here when this class is created within an AppDomain
_TimerMessage = string.Format("Timer says Started = {0} at {1}", Started, DateTime.Now);
}
}
}
消费者类
using System;
using System.Windows.Forms;
using Library;
namespace GUI
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var appDomainSetup = new AppDomainSetup
{
ApplicationName = "GUI",
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
};
_AppDomain = AppDomain.CreateDomain(appDomainSetup.ApplicationName,
AppDomain.CurrentDomain.Evidence,
appDomainSetup);
_Class1 = _AppDomain.CreateInstanceAndUnwrap("Library", "Library.Class1") as Class1;
}
private readonly AppDomain _AppDomain;
private readonly Class1 _Class1;
private void button1_Click(object sender, EventArgs e)
{
_Class1.Start();
MessageBox.Show(_Class1.GetMessage());
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show(_Class1.GetMessage());
}
}
}
运行上面的代码时,GetMessage()
始终返回:
Class1表示Started = True,Timer消息尚未设置
但是,如果我更改为上面表单的构造函数来创建Class1的本地实例,
public Form1()
{
InitializeComponent();
_Class1 = new Class1();
}
GetMessage()
返回预期的消息:
Class1表示Started = True,Timer在11/15/2011 12:34:06 PM表示Started = True
我搜索了Google,MSDN和SO,但未找到任何专门针对AppDomain,Serialization和System.Threading.Timer组合的信息。我也无法找到有关TimerCallback无法引用实例化Timer的本地成员的任何信息。
答案 0 :(得分:3)
大多数情况下,它是由“按价值编组”(您的班级)和“按参考编组”(很可能是您想要的)之间的差异造成的。如果类不是从MarshalByRefObject派生而是在远程处理时表现得像值类型,则意味着您可以在通信的每一侧获得对象的复制。如果type派生自MarshalByRefObject而不是在没有实例化该对象的一方获得代理,那么该方将能够在另一个AppDomain中的实例上调用方法。
链接:
MarshalByRefObject - http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject.aspx
MSDN杂志中的跨应用程序域调用期间的终身管理 - 下载2003年12月号MSDN magazine(您可能需要取消阻止文件属性中的内容)或使用Web存档链接Managing the Lifetime of Remote .NET Objects with Leasing and Sponsorship
答案 1 :(得分:0)
TimerMethod
中的评论说:
// Started is always false here when this class is created within an AppDomain
但是你的输出表明Started
是真的。
这是什么?
实际上,当你创建一个本地实例时,我很惊讶它是有效的。 Class1
构造函数创建计时器并为其提供回调,但它不设置间隔或到期时间,这意味着计时器不会触发。
当您致电Start
时,计时器已初始化,但是给定时间为1秒。 Start
返回,您致电GetMessage
以获取消息。但是如果你在计时器有机会执行其回调之前调用GetMessage
,你将会得到你描述的行为。
如果你在拨打Start
和拨打GetMessage
之间有1秒的延迟,我想你会发现问题是......时间:你正试图让在计时器有机会设置它之前的消息。请尝试以下方法进行验证:
private void button1_Click(object sender, EventArgs e)
{
_Class1.Start();
Thread.Sleep(1000);
MessageBox.Show(_Class1.GetMessage());
}
或者,我想你可以在几秒钟的延迟后再次点击按钮。