我的情况与此类似:
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,并且应忽略所有其他代码,包括检查。是这样吗?
"这个 CIL 代码虽然看起来效率很低,但以后在运行时被 JIT 优化了吗?" - 不,似乎结果 JITted 代码也很臃肿,但需要更多的测试来验证。我的小 4.8 框架编译if (value is T2 val)
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
这是使用 ValueType 的限制之一 - 此类操作变得一团糟。如果这对您来说是一个常见的用例,也许这不应该是一个结构体?