C# Dynamic關鍵字之:dynamic為什麼比反射快的詳解

來源:互聯網
上載者:User

Main方法如下:

複製代碼 代碼如下:static void Main(string[] args)
{
dynamic str = "abcd";
Console.WriteLine(str.Length);

Console.WriteLine();
Console.WriteLine(str.Substring(1));

Console.ReadLine();
}

運行,結果如下:

使用reflactor 反編譯下,可以看到:

完整代碼如下:

複製代碼 代碼如下:private static void Main(string[] args)
{
object obj1 = "abcd";
if (Program.<Main>o__SiteContainer0.<>p__Site1 == null)
{
Program.<Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
}
if (Program.<Main>o__SiteContainer0.<>p__Site2 == null)
{
Program.<Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Length", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
}
Program.<Main>o__SiteContainer0.<>p__Site1.Target(Program.<Main>o__SiteContainer0.<>p__Site1, typeof(Console), Program.<Main>o__SiteContainer0.<>p__Site2.Target(Program.<Main>o__SiteContainer0.<>p__Site2, obj1));
Console.WriteLine();
if (Program.<Main>o__SiteContainer0.<>p__Site3 == null)
{
Program.<Main>o__SiteContainer0.<>p__Site3 = CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "WriteLine", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
}
if (Program.<Main>o__SiteContainer0.<>p__Site4 == null)
{
Program.<Main>o__SiteContainer0.<>p__Site4 = CallSite<Func<CallSite, object, int, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "Substring", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
}
Program.<Main>o__SiteContainer0.<>p__Site3.Target(Program.<Main>o__SiteContainer0.<>p__Site3, typeof(Console), Program.<Main>o__SiteContainer0.<>p__Site4.Target(Program.<Main>o__SiteContainer0.<>p__Site4, obj1, 1));
Console.ReadLine();
}

首先編譯器會自動產生一個靜態類:如下:複製代碼 代碼如下:[CompilerGenerated]
private static class <Main>o__SiteContainer0
{
// Fields
public static CallSite<Action<CallSite, Type, object>> <>p__Site1;
public static CallSite<Func<CallSite, object, object>> <>p__Site2;
public static CallSite<Action<CallSite, Type, object>> <>p__Site3;
public static CallSite<Func<CallSite, object, int, object>> <>p__Site4;
}

為什麼這裡有四個CallSite<T>的對象呢?在我們的代碼中

Console.WriteLine(str.Length);
Console.WriteLine();
Console.WriteLine(str.Substring(1));
一共使用了四次dynamic對象。1:Console.WriteLine(dynamic); str.Length返回dynamic2:dynamic.Length;3:Console.WriteLine(dynamic); str.Substring 返回dynamic4:dynamic.Substring(1); 1,2,3,4,分別對應上面的<>p_Site1,2,3,4;

因為1,3 都是無傳回值的,所以是Action, 2,4都有傳回值,所以是Func. 看上面的代碼可能還不清楚,讓我們手動的產生下代碼吧:建立類SiteContainer 來取代編譯器自動產生的類。

複製代碼 代碼如下:[CompilerGenerated]
public static class SiteContainer
{
// Fields
public static CallSite<Action<CallSite, Type, object>> p__Site1;
public static CallSite<Func<CallSite, object, object>> p__Site2;
public static CallSite<Action<CallSite, Type, object>> p__Site3;
public static CallSite<Func<CallSite, object, int, object>> p__Site4;
}

接著看下初始化p__Site1時的方法吧:複製代碼 代碼如下:if (SiteContainer.p__Site1 == null)
{
CallSiteBinder csb= Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.ResultDiscarded,
"WriteLine", null, typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});
SiteContainer.p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(csb);
}

InvokeMember方法的簽名:public static CallSiteBinder InvokeMember(CSharpBinderFlags flags, string name, IEnumerable<Type> typeArguments, Type context, IEnumerable<CSharpArgumentInfo> argumentInfo); 1:在這裡CSharpBinderFlags傳遞的是ResultDiscarded,代表結果被丟棄, 所以可以綁定到一個void的返回方法中。2:name傳遞的是”WriteLine”,要調用的方法的名稱。3:typeArguments.型別參數的列表,傳遞null。4:context: 用於指示此操作的使用位置的 System.Type,在這裡是Program5:argumentInfo:參數資訊, 接著看看p__Site2如何初始化的吧:複製代碼 代碼如下:if (SiteContainer.p__Site2 == null)
{
CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
CSharpBinderFlags.None, "Length", typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});

SiteContainer.p__Site2 = CallSite<Func<CallSite, object, object>>.Create(csb);
}

可以看到,和p__Site1不同的是,調用的是GetMember方法。
 
既然有了兩個CallSite<T>的對象,那麼它們又是如何調用的呢??
使用CallSite<T>.Target 就可以調用了。
 

 
//這是編譯器產生的程式碼://SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), // SiteContainer.p__Site2.Target(SiteContainer.p__Site2, obj1) //); var pSite2Result = SiteContainer.p__Site2.Target(SiteContainer.p__Site2, obj1); SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), pSite2Result);

 
看看如何調用的吧:
因為SiteContainer.p__Site2,是調用Length屬性
首先調用p__Site2的target方法,執行p__Site2,對象是obj1.
dlr 就會調用obj1.Length,並返回結果,所以pSite2Result=4;
接著調用p__Site1的target,來調用Console類的WriteLine方法,參數是pSite2Result.所以輸出4.
 
最後來看下dynamic是如何調用Substring方法的:

Substring方法對應的是p__Site4,因為Substring方法傳遞了個參數1,並且有傳回值,所以

p__Site4對象是:

public static CallSite<Func<CallSite, object, int, object>> p__Site4;
初始化:

複製代碼 代碼如下:if (SiteContainer.p__Site4 == null)
{
CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.None, "Substring", null, typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant
| CSharpArgumentInfoFlags.UseCompileTimeType, null)
});
SiteContainer.p__Site4 = CallSite<Func<CallSite, object, int, object>>.Create(csb);
}

基本和上面的p__Site1類似,只是參數資訊是:CSharpArgumentInfoFlags.Constant \

因為調用了Substring(1).在編譯的時候會傳遞1進去,而1是常量。 調用如下:

複製代碼 代碼如下:var subStringResult = SiteContainer.p__Site4.Target(SiteContainer.p__Site4, obj1, 1);
SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), subStringResult);

解釋同上。

完整的Main函數代碼如下:

複製代碼 代碼如下:static void Main(string[] args)
{
object obj1 = "abcd";

if (SiteContainer.p__Site1 == null)
{
CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.ResultDiscarded,
"WriteLine", null, typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});
SiteContainer.p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(csb);
}

if (SiteContainer.p__Site2 == null)
{
CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
CSharpBinderFlags.None, "Length", typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});

SiteContainer.p__Site2 = CallSite<Func<CallSite, object, object>>.Create(csb);
}

if (SiteContainer.p__Site4 == null)
{
CallSiteBinder csb = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(
CSharpBinderFlags.None, "Substring", null, typeof(Program),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null)
});
SiteContainer.p__Site4 = CallSite<Func<CallSite, object, int, object>>.Create(csb);
}

var lengthResult = SiteContainer.p__Site2.Target(SiteContainer.p__Site2, obj1);
SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), lengthResult);

var subStringResult = SiteContainer.p__Site4.Target(SiteContainer.p__Site4, obj1, 1);
SiteContainer.p__Site1.Target(SiteContainer.p__Site1, typeof(Console), subStringResult);

Console.ReadLine();
}

在這裡,我沒有使用p__Site3,因為p__Site3和p__Site1相同,不過為什麼微軟會產生4個CallSite<T>對象,因為1 和3是完全相同的,難道是為了實現簡單?? 、幸虧還有延遲初始化,否則靜態欄位這麼多,不知道會對系統產生什麼影響 運行,
結果如下:

從這個例子也可以知道為什麼dynamic會比反射的速度要快了。
1:if(p__Site1)==null,p__Site1==xxx,並且p__Site1是靜態類中的靜態欄位,所以有緩衝效果。
2:CallSite<T>.Target: 0 級緩衝 - 基於網站記錄專用的委託.
使用委託來調用,自然比反射又要快一些。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.