我有类似的情况:
interface IStorage
{
bool TryGetValue<T>(out T result) where T : struct;
}
class Storage<T> : IStorage where T : struct
{
readonly T value;
public Storage(T val)
{
value = val;
}
public bool TryGetValue<T2>(out T2 result) where T2 : struct
{
if(value is T2 val)
{
result = val;
return true;
}
result = default;
return false;
}
}
在程序中,传递实现IStorage
的实例,可以查询它们的特定类型的值。我可以使用类似IStorage<T>
的类型并对其进行类型测试以检查其是否支持该类型,但是由于还有其他实现IStorage
的类型可以决定它们在运行时是否支持该类型,因此这会使代码变得更加混乱。
现在我想知道value is T2 val
。其目的是检查T
和T2
是否为相同类型(并因此兼容),因为它们都是值类型。 T
专用于TryGetValue
,应返回true
,对于所有其他类型,应返回false
。
我不确定这是否是检查的最佳实现。解决问题基本上有两个常规步骤:
T
和T2
是否相同。value
重新解释为T2
并返回。可以考虑解决此问题的其他两种解决方案:将值强制转换为object
并检查并取消装箱,或选择__refvalue(__makeref(value), T2)
,但可能不能保证在所有平台上都可以使用。
现在value is T2 val
看起来很漂亮,传达了很好的含义,但是我也想知道性能影响和可能的优化。当我反汇编该方法时,它变成这样:
.locals init ([0] !!T2 val, [1] !T V_1) IL_0000: ldarg.0 IL_0001: ldfld !0 value IL_0006: dup IL_0007: stloc.1 IL_0008: box !T IL_000d: isinst !!T2 IL_0012: brfalse.s IL_0029 IL_0014: ldloc.1 IL_0015: box !T IL_001a: unbox.any !!T2 IL_001f: stloc.0 IL_0020: ldarg.1 IL_0021: ldloc.0 IL_0022: stobj !!T2 IL_0027: ldc.i4.1 IL_0028: ret IL_0029: ldarg.1 IL_002a: initobj !!T2 IL_0030: ldc.i4.0 IL_0031: ret
因此,表达式不仅将值一次装箱,而且两次装箱,第一次是
isinst
,第二次是unbox.any
,所以它不仅隐藏了装箱(通常被认为是非常昂贵的),但它执行了两次。
我有两个问题:是否有更好的方法来实现这种专业化?尽管看起来CIL代码效率很低,但是否有可能稍后在运行时通过JIT对其进行优化?
[在这种情况下,我希望运行时推断出T
为T2
的唯一实例应该返回true,并且它应该忽略所有其他代码,包括检查。可能是这样吗?
我有类似的情况:接口IStorage {bool TryGetValue
“这个CIL代码虽然看起来效率很低,但稍后可能在运行时由JIT优化吗?” -不,似乎结果JITted代码也很肿,但需要更多测试才能验证。我的小4.8框架编译为if (value is T2 val)
to
00007FFDEF110E3D mov rdx,qword ptr [rbp+90h]
00007FFDEF110E44 add rdx,8
00007FFDEF110E48 vmovdqu xmm0,xmmword ptr [rdx]
00007FFDEF110E4D vmovdqu xmmword ptr [rbp+40h],xmm0
00007FFDEF110E53 lea rdx,[rbp+40h]
00007FFDEF110E57 mov rcx,7FFDEF006C68h
00007FFDEF110E61 call 00007FFE4E642570
00007FFDEF110E66 mov qword ptr [rbp+30h],rax
00007FFDEF110E6A mov rdx,qword ptr [rbp+30h]
00007FFDEF110E6E mov rcx,7FFDEF006C68h
00007FFDEF110E78 call 00007FFE4E643D00
00007FFDEF110E7D test rax,rax
00007FFDEF110E80 je 00007FFDEF110ED7
00007FFDEF110E82 lea rdx,[rbp+40h]
00007FFDEF110E86 mov rcx,7FFDEF006C68h
00007FFDEF110E90 call 00007FFE4E642570
00007FFDEF110E95 mov qword ptr [rbp+28h],rax
00007FFDEF110E99 mov rdx,qword ptr [rbp+28h]
00007FFDEF110E9D mov rcx,7FFDEF006C68h
00007FFDEF110EA7 call 00007FFE4E643D00
00007FFDEF110EAC mov qword ptr [rbp+20h],rax
00007FFDEF110EB0 mov rdx,qword ptr [rbp+20h]
00007FFDEF110EB4 mov rcx,7FFDEF006C68h
00007FFDEF110EBE call 00007FFE4E6BC030
00007FFDEF110EC3 vmovdqu xmm0,xmmword ptr [rax]
00007FFDEF110EC8 vmovdqu xmmword ptr [rbp+58h],xmm0
00007FFDEF110ECE mov dword ptr [rbp+38h],1
00007FFDEF110ED5 jmp 00007FFDEF110EDC
00007FFDEF110ED7 xor eax,eax
00007FFDEF110ED9 mov dword ptr [rbp+38h],eax
00007FFDEF110EDC mov eax,dword ptr [rbp+38h]
00007FFDEF110EDF movzx eax,al
00007FFDEF110EE2 mov dword ptr [rbp+54h],eax
00007FFDEF110EE5 cmp dword ptr [rbp+54h],0
00007FFDEF110EE9 je 00007FFDEF110F08