CLR via C# 讀書筆記 6-2 不同AppDomain之間的通信
跨AppDomain通信有兩種方式
1.Marshal By reference : 傳遞引用
2.Marshal By Value : 把需要傳遞的對象 通過序列化反序列化的方式傳遞過去(值拷貝)
只有標記為 可序列化 Serializable 的類才能通過 Marshal By Value的方式通信
以下代碼描述了幾種跨域通信的情況
1.AppDomain是CLR的內部行為,windows完全不清楚有AppDomain的存在
2.在新的域中加載Assembly和Type最好用完整限定名(如果直接加載Type, CLR會自動加載Type所在的和所用到的Assembly)
3.默認情況下新建的應用程序域使用和當前域一樣的權限設置,如果你需要手動指定權限,那么構造一個PermissionSet參數(shù)傳給CreateDomain
4.同樣的,如果想給應用程序設置不同的配置,構造一個AppDomainSetup傳給他(可以設置配置文件,程序路徑,影像拷貝什么的..)
5.跨域訪問的時候不會發(fā)生線程的上下文切換
6.CreateInstanceAndUnwrap 默認調用對象的無參構造函數(shù),當然,有些重載允許你調用有參構造函數(shù)
7.只有繼承了System.MarshalByRefObject的對象才能以引用方式傳遞 (這是一個基類....c#又不允許多繼承....還是挺麻煩的)
8.只有標記了了Serializable 的對象才能以值方式傳遞(內部的序列化反序列化) ,這里有比較嚴重的性能損耗
代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Runtime.Remoting;
namespace TestConsole
{
public class Program
{
static void Main(string[] args)
{
// 獲取一個指向應用程序域的引用
AppDomain adCallingThreadDomain = Thread.GetDomain();
// 每一個應用程序域都會被分配一個名字幫助調試 ,以下代碼獲取引用程序域的名字
String callingDomainName = adCallingThreadDomain.FriendlyName;
// 獲取應用程序域的完整名
String exeAssembly = Assembly.GetEntryAssembly().FullName;
AppDomain ad2 = null;
// ************************************************************************************************************
// 使用Marshal-by-Reference的方式跨域通信
Console.WriteLine("{0}Demo #1", Environment.NewLine);
// 建立一個域,安全和配置均使用當前域的設置
ad2 = AppDomain.CreateDomain("AD #2", null, null);
MarshalByRefType mbrt = null;
// 加載Assembly到新的域,new一個對象并且返回到當前域 (實際上返回的是一個引用代理)
mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "TestConsole.MarshalByRefType");
Console.WriteLine("Type={0}", mbrt.GetType()); // CLR 對GetType做了一些手腳,返回的是被代理數(shù)據(jù)的真實類型
// 以下代碼證明我們獲取的是一個代理對象
Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
// 我們調用了代理類的SomeMethod() , 代理類跨域訪問真正的對象
mbrt.SomeMethod();
// 卸載新建的那個應用程序域
AppDomain.Unload(ad2);
// mbrt refers to a valid proxy object; the proxy object refers to an invalid AppDomain
try
{
// 再次調用代理類的SomeMethod() , 由于域已經(jīng)被卸載 拋出一個異常
mbrt.SomeMethod();
Console.WriteLine("Successful call.");
}
catch (AppDomainUnloadedException)
{
Console.WriteLine("Failed call.");
}
// ************************************************************************************************************
// 使用Marshal-by-Value 的方式跨域通信
Console.WriteLine("{0}Demo #2", Environment.NewLine);
// 新建域
ad2 = AppDomain.CreateDomain("AD #2", null, null);
// 加載程序集并創(chuàng)建代理類
mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "TestConsole.MarshalByRefType");
// 該方法返回了一個值的拷貝 marshaled by value (not be reference).
MarshalByValType mbvt = mbrt.MethodWithReturn();
// 證明返回值不是一個代理類
Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbvt));
// 查看返回值是誰創(chuàng)建的
Console.WriteLine("Returned object created " + mbvt.ToString());
// 卸載應用程序域
AppDomain.Unload(ad2);
// 由于是值傳遞,那么卸載域對函數(shù)沒有影響 // marshaled by value
try
{
//不會有異常拋出
Console.WriteLine("Returned object created " + mbvt.ToString());
Console.WriteLine("Successful call.");
}
catch (AppDomainUnloadedException)
{
Console.WriteLine("Failed call.");
}
// ************************************************************************************************************
// non-marshalable type 跨域通信
Console.WriteLine("{0}Demo #3", Environment.NewLine);
ad2 = AppDomain.CreateDomain("AD #2", null, null);
mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "TestConsole.MarshalByRefType");
// 沒有標記為 Serializable 的類型對象 不能通過marshaled by value 跨域通信
NonMarshalableType nmt = mbrt.MethodArgAndReturn(callingDomainName);
}
}
//即使沒有標記為Serializable 也可以通過 marshaled-by-reference 的方式跨域通信
public sealed class MarshalByRefType : MarshalByRefObject
{
public MarshalByRefType()
{
Console.WriteLine("{0} ctor running in {1}",
this.GetType().ToString(), Thread.GetDomain().FriendlyName);
}
public void SomeMethod()
{
Console.WriteLine("Executing in " + Thread.GetDomain().FriendlyName);
}
public MarshalByValType MethodWithReturn()
{
Console.WriteLine("Executing in " + Thread.GetDomain().FriendlyName);
MarshalByValType t = new MarshalByValType();
return t;
}
public NonMarshalableType MethodArgAndReturn(String callingDomainName)
{
// NOTE: callingDomainName is [Serializable]
Console.WriteLine("Calling from ‘{0}’ to ‘{1}’.",
callingDomainName, Thread.GetDomain().FriendlyName);
NonMarshalableType t = new NonMarshalableType();
return t;
}
}
//只有標記為 Serializable 的類型 才能用marshaled by value 的方式跨域通信
[Serializable]
public sealed class MarshalByValType : Object
{
private DateTime m_creationTime = DateTime.Now; // NOTE: DateTime is [Serializable]
public MarshalByValType()
{
Console.WriteLine("{0} ctor running in {1}, Created on {2:D}",
this.GetType().ToString(),
Thread.GetDomain().FriendlyName,
m_creationTime);
}
public override String ToString()
{
return m_creationTime.ToLongDateString();
}
}
// 沒有標記為 Serializable 的類型 不能用marshaled by value 的方式跨域通信
// [Serializable]
public sealed class NonMarshalableType : Object
{
public NonMarshalableType()
{
Console.WriteLine("Executing in " + Thread.GetDomain().FriendlyName);
}
}
}

浙公網(wǎng)安備 33010602011771號