社区及其启动 (转自MSDN)
来源:岁月联盟
时间:2003-07-12
Eric Gunnerson
Microsoft Corporation
2002 年 3 月 5 日
在这个月的专栏中,我将着重介绍信息,只涉及一小部分代码。部分原因是有一些重要信息要和大家分享(这并不是在向您推销保险),但主要原因是工作太忙,没有时间编写代码。
社区
我曾经发起将 C# 产品周期作为 C# 编译器的测试线索。由于这个原因,我加入了 C# 语言设计组,写了本关于 C# 的书,做了很多关于 C# 的演讲,最终成为新成立的 C# 团队的项目经理,负责整个 C# 社区。
对社区进行描述是一件非常简单的事情 - 就是一群志趣相投的人在一起相互交流。在过去几年中,我曾参与火箭发射,去年夏天我决定建造一支高能火箭(详细信息,请参阅我们组的 Web 页,我将在以后的文章中讨论这个问题)。成功地建造和发射这样的火箭要求具备相当丰富的知识和经验。庆幸的是,我是一个有关火箭学的邮件列表的成员,因此我的所有问题都可以得到解答。这就是一个关于社区的绝妙范例,我希望我能把这种美妙的体验带到 C# 社区中。
这就是您加入我们的理由。我们有很多好主意,能使工作变得更轻松,但我们还需要您来帮助我们,以确保我们正在以正确的方式做正确的事情。在社区工作方面,我的总体目标是为您提供有价值的信息,并使工作充满乐趣。
我们当前的首要任务就是在 GotDotNet 上建立一个新的 C# 社区 Web 站点 http://www.gotdotnet.com/team/csharp(英文)。目前站点上的内容还不多,但所有内容都很有用,其中包括一个很棒的关于 C# 语言的常见问题列表。
我对这个站点的外观和布局感到非常自豪。我希望能这么说,但事实并非如此,因为我自己只用了几天时间设计这个站点,而我的 Web 设计技术侧重于功能方面。这个网站“会”变得更好看(也应该如此),但我会将精力集中在内容而不是主题上,所以请多多包涵。
为完成“启动 Web 站点”这一主题,我在站点上发布了关于第一个视频游戏(太空大战!)的 C# 实现。请下载代码和可执行文件,仔细研究它们,并邀您的同事和朋友加盟。同时,我们还希望您接受用户调查。
除了 GotDotNet 上的站点,我们还有一个独立的新 C# 站点 http://www.csharp.org(英文)。我在这个站点上花的时间还不是很多,但我发现其中的内容都很有用。
最后,除了 GotDotNet Web 站点,您还可以在 Microsoft 公共新闻组和其他论坛中看到更多的 Visual C#® 产品组成员。
AppDomains
最近,我正在编写一些代码来处理 AppDomains 问题。AppDomains 提供了一种方法,可以将程序的一部分与另一部分分开。简单地说,它使您可以在两个独立的环境中运行。有时,这两个环境可能位于不同的计算机上,AppDomains 之间的通信通过远程进行;在其他情况下,它们可能处于同一进程中。
除提供隔离功能外,AppDomains 还提供了一个重要的附加功能。如果 AppDomain 被卸载,它加载的程序集也将被卸载。这样可以实现系统的动态更新。例如,服务器进程可以检测到已更新的程序集的到达时间,关闭旧程序集并开始使用新的程序集。
对 AppDomains 的处理在刚开始时显得有点怪。因为从当前的 AppDomain 中分离出了一个单独的 AppDomain,您不能在它们之间自由地传递对象。它们需要被封送。您需要告诉新的 AppDomain 创建一个新对象,并为其返回一个代理,然后通过该代理进行调用。
代理对象必须来自 MarshalByRefObject,因为这是代理的实现方式。我们将使用下面的代理对象来加载程序集并对其进行某些处理:
namespace AppDomainAssemblyLoader
{
public class AssemblyProcessor : MarshalByRefObject
{
Assembly assembly;
public AssemblyProcessor()
{
}
public void Load(string assemblyName)
{
assembly = Assembly.Load(assemblyName);
}
public void Process()
{
}
}
}
这个类只提供了使用 Assembly.Load() 加载程序集的方法,以及进行处理的方法。该类必须被编译到一个独立的程序集中,稍后我将解释其原因。
代码的真正内容位于创建新 AppDomain 的类中:
public class AssemblyLoader: IDisposable
{
AppDomain appDomain;
public AssemblyLoader()
{
}
void Dispose(bool disposing)
{
if (appDomain != null)
{
AppDomain.Unload(appDomain);
appDomain = null;
}
}
~AssemblyLoader()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
public void ProcessAssembly(string filename)
{
FileInfo fileInfo = new FileInfo(filename);
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = fileInfo.DirectoryName;
setup.PrivateBinPath =
AppDomain.CurrentDomain.BaseDirectory;
setup.ApplicationName = "Loader";
setup.ShadowCopyFiles = "true";
try
{
appDomain = AppDomain.CreateDomain(
"Loading Domain", null, setup);
AssemblyProcessor processor = (AssemblyProcessor)
appDomain.CreateInstanceFromAndUnwrap(
"AppDomainAssemblyLoader.dll",
"AppDomainAssemblyLoader.AssemblyProcessor");
string name =
fileInfo.Name.Replace(fileInfo.Extension, "");
Console.WriteLine(name);
processor.Load(name);
processor.Process();
}
finally
{
if (appDomain != null)
{
AppDomain.Unload(appDomain);
appDomain = null;
}
}
}
}
AppDomain 的安装是一个非常关键的部分。AssemblyProcessor 类使用 Assembly.Load() 定位程序集,它在 AppDomain 的应用程序库中寻找程序集。因此,我们需要将应用程序库设置为程序集所在的目录。这可以通过以下命令行来完成:
setup.ApplicationBase = fileInfo.DirectoryName;
注意:AppDomain 可以通过与 AssemblyResolve 事件相关联来实现自身的加载规则。
但该代码不能用于加载 AssemblyProcessor 类,因为它与当前的 AppDomain 位于同一目录。因此,我们可以将新 AppDomain 的专用二进制路径设置为当前应用程序域的目录。
setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
只需再配置一项内容。最后,我们希望用户能够重写我们的程序集,不过,所有程序集在加载时都已被锁定。解决办法是在其他目录中存放一个程序集副本,这样,原始程序集就不会被锁定了。AppDomain 可以自动完成这项工作:
setup.ShadowCopyFiles = "true";
在定义了 AppDomain 的安装方式之后,我们就可以创建 AppDomain,然后在新的 AppDomain 中创建一个 AssemblyProcessor 实例:
AssemblyProcessor processor = (AssemblyProcessor)
appDomain.CreateInstanceFromAndUnwrap(
"AppDomainAssemblyLoader.dll",
"AppDomainAssemblyLoader.AssemblyProcessor");
我们调用 CreateInstanceFromAndUnwrap(),传递 CreateInstanceFromAndUnwrap() 要使用的程序集的文件名,以及我们希望它创建的类型的完全限定的名称。
完成上述操作后,我们将在新创建的 AppDomain 中得到一个针对真正的 AssemblyProcessor 对象的 AssemblyProcessor 代理。我们可以将它用作本地对象:
string name = fileInfo.Name.Replace(fileInfo.Extension, "");
Console.WriteLine(name);
processor.Load(name);
processor.Process();
由于 Assembly.Load() 需要程序集名称但不会处理文件名,所以必须除去扩展名。
处理结束后,AppDomain 将被卸载,这将清除所有内容。我还实现了一个析构函数和 IDisposable 接口(尽管这个简单示例并不需要它们)。
下个月
下个月,我将介绍使用此代码的示例应用程序。请访问我们的新社区站点(英文),并接受 C# 用户调查
上一篇:正确认识“Web服务”( 转)
下一篇:C#锐利体验