TL; DR;
我認爲運行時具有改善消息所需的所有信息。也許有些JIT開發人員可以提供幫助,因爲不用說JIT代碼非常敏感,並且有時候會因爲性能或安全原因而作出決定,這對於外人來說是非常難以理解的。
詳細解釋
爲了簡化我改變該方法的問題:
C#
void StringBuilderCast()
{
object sbuilder = new StringBuilder();
string s = (string)sbuilder;
}
IL
.method private hidebysig
instance void StringBuilderCast() cil managed
{
// Method begins at RVA 0x214c
// Code size 15 (0xf)
.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
} // end of method Program::StringBuilderCast
重要這裏操作碼爲:
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 | +---->| L | T | DATA |
+---------------+ | +---+---+----------+
| 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))
{
fCast = TRUE;
}
if (Nullable::IsNullableForType(toTypeHnd, obj->GetMethodTable()))
{
// allow an object of type T to be cast to Nullable<T> (they have the same representation)
fCast = TRUE;
}
// If type implements ICastable interface we give it a chance to tell us if it can be casted
// to a given type.
else if (toTypeHnd.IsInterface() && fromTypeHnd.GetMethodTable()->IsICastable())
{
...
}
if (!fCast && throwCastException)
{
COMPlusThrowInvalidCastException(&obj, toTypeHnd);
}
這裏的重要部分是拋出異常的方法。正如你所看到的 它接收當前對象和你試圖轉換的類型。
最後,投擲方法調用此方法:
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
{
// Method begins at RVA 0x2168
// Code size 15 (0xf)
.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
我們可以看到Unb oxAny行爲在這裏 https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/interpreter.cpp#L8766
//GET THE BOXED VALUE FROM THE STACK
Object* obj = OpStackGet<Object*>(tos);
//GET THE TARGET TYPE METADATA
unsigned boxTypeTok = getU4LittleEndian(m_ILCodePtr + 1);
boxTypeClsHnd = boxTypeResolvedTok.hClass;
boxTypeAttribs = m_interpCeeInfo.getClassAttribs(boxTypeClsHnd);
//IF THE TARGET TYPE IS A REFERENCE TYPE
//NOTHING CHANGE FROM ABOVE
if ((boxTypeAttribs & CORINFO_FLG_VALUECLASS) == 0)
{
!ObjIsInstanceOf(obj, TypeHandle(boxTypeClsHnd), TRUE)
}
//ELSE THE TARGET TYPE IS A REFERENCE TYPE
else
{
unboxHelper = m_interpCeeInfo.getUnBoxHelper(boxTypeClsHnd);
switch (unboxHelper)
{
case CORINFO_HELP_UNBOX:
MethodTable* pMT1 = (MethodTable*)boxTypeClsHnd;
MethodTable* pMT2 = obj->GetMethodTable();
if (pMT1->IsEquivalentTo(pMT2))
{
res = OpStackGet<Object*>(tos)->UnBox();
}
else
{
CorElementType type1 = pMT1->GetInternalCorElementType();
CorElementType type2 = pMT2->GetInternalCorElementType();
// we allow enums and their primtive type to be interchangable
if (type1 == type2)
{
res = OpStackGet<Object*>(tos)->UnBox();
}
}
//THE RUNTIME DOES NOT KNOW HOW TO UNBOX THIS ITEM
if (res == NULL)
{
COMPlusThrow(kInvalidCastException);
//I INSERTED THIS COMMENTS
//auto thCastFrom = obj->GetTypeHandle();
//auto thCastTo = TypeHandle(boxTypeClsHnd);
//RealCOMPlusThrowInvalidCastException(thCastFrom, thCastTo);
}
break;
case CORINFO_HELP_UNBOX_NULLABLE:
InterpreterType it = InterpreterType(&m_interpCeeInfo, boxTypeClsHnd);
size_t sz = it.Size(&m_interpCeeInfo);
if (sz > sizeof(INT64))
{
void* destPtr = LargeStructOperandStackPush(sz);
if (!Nullable::UnBox(destPtr, ObjectToOBJECTREF(obj), (MethodTable*)boxTypeClsHnd))
{
COMPlusThrow(kInvalidCastException);
//I INSERTED THIS COMMENTS
//auto thCastFrom = obj->GetTypeHandle();
//auto thCastTo = TypeHandle(boxTypeClsHnd);
//RealCOMPlusThrowInvalidCastException(thCastFrom, thCastTo);
}
}
else
{
INT64 dest = 0;
if (!Nullable::UnBox(&dest, ObjectToOBJECTREF(obj), (MethodTable*)boxTypeClsHnd))
{
COMPlusThrow(kInvalidCastException);
//I INSERTED THIS COMMENTS
//auto thCastFrom = obj->GetTypeHandle();
//auto thCastTo = TypeHandle(boxTypeClsHnd);
//RealCOMPlusThrowInvalidCastException(thCastFrom, thCastTo);
}
}
}
break;
}
}
嗯...至少,似乎有可能提供更好的例外信息。 如果你還記得當異常有一個不錯的消息電話是:
COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strCastFromName.GetUnicode(), strCastToName.GetUnicode());
和少inforative消息是:
COMPlusThrow(kInvalidCastException);
所以我認爲這是可以提高的消息做
auto thCastFrom = obj->GetTypeHandle();
auto thCastTo = TypeHandle(boxTypeClsHnd);
RealCOMPlusThrowInvalidCastException(thCastFrom, thCastTo);
我在coreclr github上創建了以下問題,以瞭解Microsoft開發人員的看法。
https://github.com/dotnet/coreclr/issues/7655
喬恩[已經問過這個問題(http://stackoverflow.com/questions/1583050/performance-surprise-with-as-and-nullable-types)。大致。同樣的原因,這必須在.NET 1.x時間內生成緊湊且快速的代碼。如果你想要一個很好的異常消息,那麼你必須編寫代碼'Convert.ToInt64(reference)'。仍然非常緊湊,不如一樣快。 –
@HansPassant所以你鏈接的問題是關於爲什麼模式'var nullable = box作爲int?如果(nullable.HasValue){/ *使用nullable.Value * /}'比'if(box is int){var value =(int)box;/*使用值* /}'。所以我知道在後面的例子中使用的CIL指令'unbox.any'將會很快,因爲不涉及複製。而且,在那些.NET中,只要簡單的值始終放在非泛型集合中,就必須快速。 __但它是如何回答我的問題的?__在類型檢查失敗的「分支」中,我們不能將更多細節放入異常嗎? –
此外,'castclass' CIL指令預計會非常快速,並且在那些集合不是強類型的'ArrayList'和'Hashtable'天必須如此快。 'castclass'不復制,只是類型檢查和參考去同一個地方。那麼區別在哪裏?在類型檢查失敗的情況下,'castclass'會導致在異常消息中拼寫出的類型和類型的「豐富」異常。 –