眾所周知:AppDomain.DoCallBack方法會在指定應用程式定義域中執行代碼,如果傳入委託是靜態,那麼委託正常進行。如果傳入委託是對象的成員函數,那麼這個對象會被封送(按值或者按引用)。
當傳入委託是以Lambda運算式的形式,那麼有些地方值得注意。
如果是傳入對外部變數捕獲的Lambda運算式,那麼DoCallBack方法總會拋出異常的,看下面代碼(這是一種錯誤的在另外一個應用程式定義域中建立Remoting對象的方法)
using System;
using System.Runtime.Remoting;
namespace Mgen.TTC
{
class a : MarshalByRefObject
{ }
class Program
{
static void Main()
{
var appdom = AppDomain.CreateDomain("new");
a obj = null;
appdom.DoCallBack(() =>
{
obj = new a();
});
Console.WriteLine(RemotingServices.IsTransparentProxy(obj));
}
}
}
事實上,程式不僅不會像表面上看的在新的應用程式定義域中建立一個a的對象,而是會拋出異常:
Unhandled Exception: System.Runtime.Serialization.SerializationException: Type '
Mgen.TTC.Program+<>c__DisplayClass1' in assembly 'Mgen, Version=1.0.0.0, Culture
=neutral, PublicKeyToken=null' is not marked as serializable.
at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
由於用到了對外部變數進行捕獲的Lambda運算式,所以這個Lambda會被編譯成一個嵌套在Program類的子類,然後主函數中的本地變數obj會被替換成嵌套類的一個欄位,整個Lambda的執行是嵌套類的一個普通方法。
下面是Reflector下看這個嵌套類:
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public a obj;
// Methods
public <>c__DisplayClass1()
{
base..ctor();
return;
}
public void <Main>b__0()
{
this.obj = new a();
return;
}
}
這樣的話:其實a的對象還是在本地應用程式定義域中被建立的。同時傳入DoCallBack的委託是Lambda由編譯器產生的嵌套類的方法。所以由於這個編譯器產生的嵌套類無法被按引用封送或者按值封送而由異常拋出。
當然不是所有Lambda運算式都會被編譯成嵌套類,沒有外部變數捕獲的Lambda會被直接編譯成靜態方法!
比如下面代碼(為了更好說明,在非靜態成員函數中生命Lambda)
class b
{
public void doo()
{
var appdom = AppDomain.CreateDomain("new");
appdom.DoCallBack(() => Console.WriteLine("hehe"));
}
}
class Program
{
static void Main()
{
new b().doo();
}
}
程式會順利執行(在另一個應用程式定義域中執行Console.WriteLine)。
Reflector下b類的源碼
internal class b
{
// Fields
[CompilerGenerated]
private static CrossAppDomainDelegate CS$<>9__CachedAnonymousMethodDelegate1;
// Methods
public b()
{
base..ctor();
return;
}
[CompilerGenerated]
private static void <doo>b__0()
{
Console.WriteLine("new引用");
return;
}
public void doo()
{
AppDomain appdom;
if (CS$<>9__CachedAnonymousMethodDelegate1 != null)
{
goto Label_0024;
}
CS$<>9__CachedAnonymousMethodDelegate1 = new CrossAppDomainDelegate(b.<doo>b__0);
Label_0024:
AppDomain.CreateDomain("new").DoCallBack(CS$<>9__CachedAnonymousMethodDelegate1);
return;
}
}