這個問題在這里已經有了答案: 未呼叫多載的擴展方法 1 個回答 46 分鐘前關閉。
我做了一個通用的多載來Enum.HasFlag防止拳擊:
public static unsafe bool HasFlag<T>(this T enumVal, T flag) where T : unmanaged, Enum {
return sizeof(T) switch {
1 => (*(byte*)&enumVal & *(byte*)&flag) == *(byte*)&flag,
2 => (*(ushort*)&enumVal & *(ushort*)&flag) == *(ushort*)&flag,
4 => (*(uint*)&enumVal & *(uint*)&flag) == *(uint*)&flag,
8 => (*(ulong*)&enumVal & *(ulong*)&flag) == *(ulong*)&flag,
_ => throw new ArgumentException("Unsupported base enum Type")
};
}
然而編譯器仍然想使用默認值Enum.HasFlag,我必須明確定義泛型型別以強制它使用擴展。
擴展方法在這里應該優先考慮,因為引數具有正確的型別,并且與原始方法相比不需要隱式型別轉換,那么為什么編譯器仍然使用錯誤的方法呢?
uj5u.com熱心網友回復:
有關多載解決程序的詳細資訊,請參閱規范的第 12.6.4 節。在 §12.7.6.1 中流程的一般描述的底部,您可以看到:
否則,將嘗試
E.I作為擴展方法呼叫進行處理(第 12.7.8.3 節)。如果失敗,E.I則說明成員參考無效,并且發生系結時錯誤。
如果我們看一下§12.7.8.3:
如果呼叫的正常處理未找到適用的方法,則嘗試將構造處理為擴展方法呼叫
很明顯,只有當多載解決程序未能找到適用的實體方法時,才會嘗試系結擴展方法。
這是一個深思熟慮的決定。如果不是這種情況,在檔案頂部添加一條using陳述句可能會改變方法在檔案中進一步系結的方式——在遠處的幽靈般的動作,規范通常試圖避免這種情況。
但是,自 .NET Core 2.1 以來,Enum.HasFlag它一直是 JIT 內在函式(它是引入 JIT 內在函式機制的典型代表)。這意味著盡管 IL 可能會說要裝箱并呼叫該Enum.HasFlag方法,但實際上 JIT 知道它可以用單個按位測驗來代替它。
例如,代碼:
public void Native(StringSplitOptions o) {
if (o.HasFlag(StringSplitOptions.RemoveEmptyEntries))
{
Console.WriteLine("Noo");
}
}
在 Release 中被 jitted 到這個程式集:
C.Native(System.StringSplitOptions)
L0000: test dl, 1
L0003: je short L0017
L0005: mov rcx, 0x1ac4adebda0
L000f: mov rcx, [rcx]
L0012: jmp 0x00007ffb2f6ff7f8
L0017: ret
那里沒有任何方法呼叫的跡象(除了最后的決賽Console.WriteLine)!
使用您的擴展方法的相同代碼明顯更糟:
public void Worse(StringSplitOptions o) {
if (o.HasFlag<StringSplitOptions>(StringSplitOptions.RemoveEmptyEntries))
{
Console.WriteLine("Noo");
}
}
給出:
C.Worse(System.StringSplitOptions)
L0000: sub rsp, 0x28
L0004: mov [rsp 0x24], edx
L0008: mov dword ptr [rsp 0x20], 1
L0010: mov ecx, [rsp 0x24]
L0014: and ecx, [rsp 0x20]
L0018: cmp ecx, [rsp 0x20]
L001c: sete cl
L001f: movzx ecx, cl
L0022: test ecx, ecx
L0024: je short L0038
L0026: mov rcx, 0x1ac4adebda0
L0030: mov rcx, [rcx]
L0033: call 0x00007ffb2f6ff7f8
L0038: nop
L0039: add rsp, 0x28
L003d: ret
我們可以看到這已經行內了你HasFlag方法的內容,即:
Extensions.HasFlag[[System.StringSplitOptions, System.Private.CoreLib]](System.StringSplitOptions, System.StringSplitOptions)
L0000: mov [rsp 8], ecx
L0004: mov [rsp 0x10], edx
L0008: mov eax, [rsp 8]
L000c: and eax, [rsp 0x10]
L0010: cmp eax, [rsp 0x10]
L0014: sete al
L0017: movzx eax, al
L001a: ret
由于您的問題已標記為[.net-6.0],因此最好的建議是丟棄您的擴展方法并使用內置的Enum.HasFlag,因為它比您撰寫的要快得多。
在 SharpLab 上看到這一切。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/415673.html
標籤:
上一篇:C#JsonIgnore到基礎類
