简述:
我认为运行时拥有改进消息所需的所有信息。也许一些JIT开发人员可以提供帮助,因为不必说JIT代码非常敏感,有时会因为性能或安全原因做出决策,这对外部人员来说非常难以理解。
详细说明
为了简化问题,我将方法更改为:
C#
void StringBuilderCast()
{
object sbuilder = new StringBuilder();
string s = (string)sbuilder;
}
IL
.method private hidebysig
instance void StringBuilderCast() cil managed
{
.maxstack 1
.locals init (
[0] object sbuilder,
[1] string s
)
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: castclass [mscorlib]System.String
IL_000d: stloc.1
IL_000e: ret
}
这里重要的操作码是:
http://msdn.microsoft.com/library/system.reflection.emit.opcodes.newobj.aspx
http://msdn.microsoft.com/library/system.reflection.emit.opcodes.castclass.aspx
而一般的内存布局是:
Thread Stack Heap
+
| some variable | +
+
| sbuilder2 |
+
T = Instance Type
L = Instance Lock
Data = Instance Data
在这种情况下,运行时知道它拥有一个指向StringBuilder的指针,并且应该将其转换为字符串。在这种情况下,它具有提供最佳异常所需的所有信息。
如果我们看一下JIT
https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/interpreter.cpp#L6137,我们会看到类似于这样的东西。
CORINFO_CLASS_HANDLE cls = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Casting InterpTracingArg(RTK_CastClass));
Object * pObj = OpStackGet<Object*>(idx);
ObjIsInstanceOf(pObj, TypeHandle(cls), TRUE)) //ObjIsInstanceOf will throw if cast can't be done
如果我们深入研究这个方法
https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/eedbginterfaceimpl.cpp#L1633
而重要的部分是:
BOOL fCast = FALSE;
TypeHandle fromTypeHnd = obj->GetTypeHandle();
if (fromTypeHnd.CanCastTo(toTypeHnd))
if (Nullable::IsNullableForType(toTypeHnd, obj->GetMethodTable()))
else if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsICastable())
if (!fCast && throwCastException)
这里重要的部分是抛出异常的方法。您可以看到,它接收当前对象和您尝试转换的类型。
最后,Throw方法调用此方法:
https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/excep.cpp#L13997
COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strCastFromName.GetUnicode(), strCastToName.GetUnicode());
这将为您提供带有类型名称的良好异常消息。
但是,当您将对象强制转换为值类型时
C#
void StringBuilderToLong()
{
object sbuilder = new StringBuilder();
long s = (long)sbuilder;
}
IL
.method private hidebysig
instance void StringBuilderToLong () cil managed
{
.maxstack 1
.locals init (
[0] object sbuilder,
[1] int64 s
)
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: unbox.any [mscorlib]System.Int64
IL_000d: stloc.1
IL_000e: ret
}
这里的重要操作码是:
http://msdn.microsoft.com/library/system.reflection.emit.opcodes.unbox_any.aspx
我们可以在这里看到UnboxAny的行为:
https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/interpreter.cpp#L8766
Object* obj = OpStackGet<Object*>(tos);
unsigned boxTypeTok = getU4LittleEndian(m_ILCodePtr + 1);
boxTypeClsHnd = boxTypeResolvedTok.hClass;
boxTypeAttribs = m_interpCeeInfo.getClassAttribs(boxTypeClsHnd);
if ((boxTypeAttribs & CORINFO_FLG_VALUECLASS) == 0)
else
else
}
if (res == NULL)
break;
case CORINFO_HELP_UNBOX_NULLABLE:
InterpreterType it = InterpreterType(&m_interpCeeInfo, boxTypeClsHnd);
size_t sz = it.Size(&m_interpCeeInfo);
if (sz > sizeof(INT64))
}
else
}
}
break;
}
}
好吧...至少,似乎有可能给出更好的异常消息。
如果你还记得当异常有一个好消息时的调用:
COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strCastFromName.GetUnicode(), strCastToName.GetUnicode());
而且信息较少的消息是:
COMPlusThrow(kInvalidCastException);
所以我认为可以通过改进来提高消息。
auto thCastFrom = obj->GetTypeHandle();
auto thCastTo = TypeHandle(boxTypeClsHnd);
RealCOMPlusThrowInvalidCastException(thCastFrom, thCastTo);
我已在coreclr的github上创建了以下问题,以了解微软开发人员的意见。
https://github.com/dotnet/coreclr/issues/7655
Convert.ToInt64(reference)
的代码。虽然仍然很紧凑,但不如之前快速。 - Hans Passantvar nullable = box as int?; if (nullable.HasValue) { /* use nullable.Value */ }
比模式if (box is int) { var value = (int)box; /* use value */ }
慢得多。我知道后者示例中使用的 CIL 指令unbox.any
将会很快,因为不涉及复制。而在 .NET 1 时代,简单值经常被装箱到非泛型集合中,因此必须快速。但是它如何回答我的问题呢?在类型检查失败的“分支”中,我们不能将更多详细信息放入异常中吗? - Jeppe Stig Nielsencastclass
CIL 指令被期望非常快速,在那些集合没有强类型的ArrayList
和Hashtable
时代必须如此。castclass
不进行复制,只进行类型检查,引用指向同一位置。那么区别在哪里呢?当类型检查失败时,castclass
会导致一个“丰富”的异常,其中包含了异常消息中的源类型和目标类型。 - Jeppe Stig Nielsen