Узнать размер Managed Класса (MC++,C#,...)
Eng: How can getting the size of a managed, C#, MC++ class?
Сразу оговорюсь - это не вопрос как сделать, это код на ваш суд.
Просто нигде не встретил ответа на данный вопрос(а многих он интересовал) поэтому решил собрать свой велосипед!!!
Многие говорили, что размер класса 4 и я им искренне верю(ибо класс - это ссылта и в х32 ее размер действительно 4), но меня интересовал размер класса, как если бы он был структурой(т.е. размер памяти занимаемой внутренними полями) .
{
//Тип char в Unicode, связано с тем, что char в С# - 2байта, а в С++ - 1байт
static Type^ CharType = __wchar_t::typeid;
public:
//Подгрузка словаря, чтоб последующие запросы размера данного типа были на порядок быстрее
//За идею спасибо HARDCASE -> http://forum.codenet.ru/showthread.php?t=53595
static Dictionary<Type^,int>^ DictTypeSize = gcnew Dictionary<Type^,int>();
//Вычисление размера переменной
generic<typename T>
static int sizeOf(T obj)
{
Type^ Ty = obj->GetType();
//Если объект является классом или структурой с автовыравниваем,
//Узнать его размер sizeof() мы не можем
if(Ty->StructLayoutAttribute->Value == LayoutKind::Auto)
{
int j;
if(DictTypeSize->TryGetValue(Ty,j)) //Пробуем извлечь из словаря
return j;
else
{ //Рассчитываем размеры внутренних полей
array<FieldInfo^>^ Fields = Ty->GetFields(BindingFlags::Instance | BindingFlags::Public | BindingFlags::NonPublic);
array<int>^ Arr = gcnew array<int>(Fields->Length);
j=-1;
for each(FieldInfo^ inf in Fields)
{
Type^ fieldTy = inf->FieldType;
if(fieldTy -> IsClass)
Arr[++j]=4; //Если тип поля - класс, то это ссылка 4байта
else
if(fieldTy == CharType) Arr[++j] = 2; //Если тип - char, то 2 байта
else Arr[++j] = sizeOf(fieldTy); //Вычисляем размер структуры, зная ее тип
}
j=0;
for each(int ej in Arr)
j+=ej;
if(j%4!=0 && j>2) j+=4-j%4; //Учитываем свойство коробочки(если размер >2, то он кратен 4м)
DictTypeSize->Add(Ty,j); //Записываем в словарь
return j;
}
}
else
{
return sizeof(obj);
}
}
//Вычисление размера типа данных
static int sizeOf(Type^ Ty)
{
if(Ty->StructLayoutAttribute->Value == LayoutKind::Auto)
{
int j;
if(DictTypeSize->TryGetValue(Ty,j))
return j;
else
{
array<FieldInfo^>^ Fields = Ty->GetFields(BindingFlags::Instance | BindingFlags::Public | BindingFlags::NonPublic);
array<int>^ Arr = gcnew array<int>(Fields->Length);
j=-1;
for each(FieldInfo^ inf in Fields)
{
Type^ fieldTy = inf->FieldType;
if(fieldTy -> IsClass)
Arr[++j]=4;
else
{
if(fieldTy == CharType) Arr[++j] = 2;
else Arr[++j] = sizeOf(fieldTy);
}
}
j=0;
for each(int ej in Arr)
j+=ej;
if(j%4!=0 && j>2) j+=4-j%4;
DictTypeSize->Add(Ty,j);
return j;
}
}
else return Marshal::SizeOf(Ty);
}
};
Забыл про ограничение - выравние:
Struct: Auto || Sequential (По умолчанию Sequential)
Class: Auto || Sequential (По умолчанию Auto)
Что-то не то в этом коде... Отражение конечно дело, да не всегда будет работать, я бы поискал в сторону запроса размера объекта у самой CLR - ей уж точно виднее.
Кроме того, к классу прилагается ~8 байт в заголовке.
Запихиваю функцию в сборку и работаю из С#
Сравниваю с результатами:
unsafe C# есть функция sizeof(struct)
кроме того в С# есть Marshal.SizeOf(object obj)[LayoutKind::Sequential]
Полностью согласен - она же знает скоко памяти выделить под объект, но нигде освещения данного вопроса не нашел.....
Кроме того, к классу прилагается ~8 байт в заголовке.
~4 байта - там записан HashCode - Идентификатор типа
А нужно это для:
{
if (dynamic_cast<ValueType^>(obj)==nullptr)
{
int _size = SizeOf::sizeOf(obj);
Object^ copy;
if(_size>8) copy = gcnew array<int,1>(_size/4-2);
else if(_size==4) copy = gcnew Int32();
else if(_size==2) copy = gcnew __wchar_t();
else copy = gcnew Byte();
memcpy(Native::UnBox(©),Native::UnBox(&obj),_size+4);
return copy;
}
return obj;
}
Клонирование 1000000 Объектов через
MemberwiseClone - 6,94 сек
MyClone - 1,12 сек
А нужно это для:
{
if (dynamic_cast<ValueType^>(obj)==nullptr)
{
int _size = SizeOf::sizeOf(obj);
Object^ copy;
if(_size>8) copy = gcnew array<int,1>(_size/4-2);
else if(_size==4) copy = gcnew Int32();
else if(_size==2) copy = gcnew __wchar_t();
else copy = gcnew Byte();
memcpy(Native::UnBox(©),Native::UnBox(&obj),_size+4);
return copy;
}
return obj;
}
MemberwiseClone - 6,94 сек
MyClone - 1,12 сек
Ну, клонирование такого количества объектов обычно нафиг не нужно, а вот по ссылке пройти очень советую.
Мне профайлер говорит, что 8. Правда, я подразумевал, что речь идёт о памяти, занимаемой экземпляром управляемого типа, а вы толкуете о размере той части данных управляемого объекта, которая получается в результате маршалинга в неуправляемый указатель. Это разные числа.
Идентификатор типа и хэш - тоже две большие разницы. Кроме того, хэш обычно не хранится, а вычисляется. И нужен он для ассоциативных коллекций, если вы об Object::GetHashCode().
MemberwiseClone - 6,94 сек
MyClone - 1,12 сек
Не думаю, что клонирование может стать узким местом в системе...