|
|
От: |
Serginio1
|
https://habrahabr.ru/users/serginio1/topics/ |
| Дата: | 14.09.21 08:34 | ||
| Оценка: | |||
Ага, значит TypeHandle может быть как указателем на MethodTable, так и указателем на TypeDesc, в зависимости от типа объекта. Для массивов он указывает на TypeDesc. Тип object[][] — это массив, элементами которого являются object[], для которых TypeHandle=TypeDesc. Эта информация объясняет наш пример, но всё ещё остаются некоторые вопросы. Например: а как же отличить, на что именно указывает TypeHandle? Поможет нам в этом дальнейшее изучение исходников CLI:
FORCEINLINE BOOL IsUnsharedMT() const {
LEAF_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
return((m_asTAddr & 2) == 0);
}
FORCEINLINE BOOL IsTypeDesc() const {
WRAPPER_CONTRACT;
return(!IsUnsharedMT());
}Всё зависит от второго бита в адресе: нулевое значение определяет MethodTable, а единичное — TypeDesc. Если мы работаем с шестнадцатеричными адресами, то можно легко определить вид TypeHandle по последней цифре:
MethodTable: 0, 1, 4, 5, 8, 9, C, D
TypeDesc : 2, 3, 6, 7, A, B, E, F
А теперь взглянем ещё раз на дамп памяти нашего примера. Можно видеть, что для System.Object[] в дампе присутствуют указатели как на его TypeDesc, так и на MethodTable. Не смотря на то, что под TypeHandle в данном случае подразумевается TypeDesc, заголовочный указатель для a[0] всё-таки указывает на MethodTable. Поэтому некорректно говорить о том, что в заголовке каждого объекта хранится TypeHandle: там хранится указатель на MethodTable, а это далеко не всегда одно и то же.
Пример 4
Последний пример проиллюстрирует недавно полученное правило про последнюю цифру адреса. Мы можем получить TypeHandle прямо из управляемого кода, а по этому значению мы можем определить, что именно под ним подразумевается:
private void Run()
{
Print(typeof(int));
Print(typeof(object));
Print(typeof(Stream));
Print(typeof(int[]));
Print(typeof(int[][]));
Print(typeof(object[]));
}
private void Print(Type type)
{
bool isTypeDesc = ((int)type.TypeHandle.Value & 2) > 0;
Console.WriteLine("{0}: {1} => {2}",
type.Name.PadRight(10),
type.TypeHandle.Value.ToString("X"),
(isTypeDesc ? "TypeDesc" : "MethodTable"));
}У меня этот код выводит следующее:
Int32 : 65C4C480 => MethodTable
Object : 65C4B060 => MethodTable
Stream : 65C4D954 => MethodTable
Int32[] : 65854C8A => TypeDesc
Int32[][] : 658F6BD6 => TypeDesc
Object[] : 65854D7A => TypeDesc
Выводы
В ходе нашего маленького исследования были получены следующие выводы:
TypeHandle является указателем либо на MethodTable, либо на TypeDesc (зависит от типа объекта)
В заголовке каждого объекта для идентификации его типа всегда хранится указатель на MethodTable (это не всегда TypeHandle)
Для массивов, чьи элементы должны представлять ссылочный тип, хранится дополнительное поле, которое представляет собой TypeHandle для типа элементов.