/// <summary> /// Генерирует объект с метаданными типа в соответствии с заданными атрибутами <see cref="AmfObjectAttribute"/>, с полями типа, заданного в атрибутах <see cref="AmfMemberAttribute"/>. /// </summary> /// <param name="sourceObject">Исходный экземпляр объекта.</param> /// <returns></returns> private static object GenerateType <T>(T sourceObject) { Type sourceType = sourceObject.GetType(); // Получаем метаданные типа исходного объекта. if (sourceType.IsDictionary()) { return(GenerateType(sourceObject as IEnumerable <KeyValuePair <string, object> >)); } if (!sourceType.IsDefinedAttribute <AmfObjectAttribute>()) { return(sourceObject); // Если у типа объекта не задан атрибут - возвращаем как есть. } string typeName = sourceType.GetAttribute <AmfObjectAttribute>().Name ?? sourceType.FullName; // Определяем имя у типа. Type definedType = moduleBuilder.GetType(typeName); // Пытаемся найти уже определенный в сборке тип. TypeBuilder typeBuilder = null; // Определяем билдер для нашего типа. Dictionary <string, object> properties = new Dictionary <string, object>(); // Словарь свойств объекта. Dictionary <string, object> fields = new Dictionary <string, object>(); // Словарь полей объекта. // Если тип в сборке еще не определен... if (definedType == null) { typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public); // Опледеляем тип с нашим именем. ConstructorBuilder ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); // Определяем конструктор. ILGenerator ctorIL = ctor.GetILGenerator(); // Получаем ссылку на генератор MSIL-инструкций для конструктора. ctorIL.Emit(OpCodes.Ldarg_0); // Помещаем в стек вычислений нулевой аргумент. ctorIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); // Вызываем базовый конструктор для инициализации значения по умолчанию у нулевого аргумента. ctorIL.Emit(OpCodes.Ret); // Помещаем в стек вычислений инструкцию о возврате из метода. // Перебираем все свойства нашего типа. foreach (PropertyInfo propertyInfo in sourceType.GetProperties()) { AmfMemberAttribute attribute = propertyInfo.GetAttribute <AmfMemberAttribute>(); // Получаем наш кастомный атрибут типа AmfMemberAttribute. if (attribute == null) { continue; // Если атрибут не указан - пропускаем свойство. } string propertyName = attribute.Name ?? propertyInfo.Name; // Получаем имя свойства. object propertyValue = propertyInfo.GetValue(sourceObject, null); // Получаем значение свойства. Type propertyType = propertyInfo.PropertyType; // Получаем метаданные типа свойства. // Если у типа задан атрибут или это словарь... if (propertyInfo.PropertyType.IsDefinedAttribute <AmfObjectAttribute>() || propertyType.IsDictionary()) { // Генерируем объект типа, заданного в атрибуте. propertyValue = propertyType.IsDictionary() ? GenerateType(propertyValue as IEnumerable <KeyValuePair <string, object> >) : GenerateType(propertyValue); propertyType = propertyValue.GetType(); // Обновляем тип свойства. } FieldBuilder fieldBuilder = typeBuilder.DefineField($"m_{propertyName}", propertyType, FieldAttributes.Private); // Определяем новое приватное поле. PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); // Определяем новое свойство. MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; // Устанавливаем атрибуты аксессору и мутатору свойства. MethodBuilder methodBuilderAccessor = typeBuilder.DefineMethod($"get_{propertyName}", getSetAttr, propertyType, Type.EmptyTypes); // Определяем аксессор. ILGenerator accessorIL = methodBuilderAccessor.GetILGenerator(); // Получаем ссылку на генератор MSIL-инструкций для аксессора. accessorIL.Emit(OpCodes.Ldarg_0); // Помещаем в стек вычислений нулевой аргумент. accessorIL.Emit(OpCodes.Ldfld, fieldBuilder); // Помещаем в стек вычислений инструкцию о получении значения по ссылке поля. accessorIL.Emit(OpCodes.Ret); // Помещаем в стек вычислений инструкцию о возврате из метода. MethodBuilder methodBuilderSetter = typeBuilder.DefineMethod($"set_{propertyName}", getSetAttr, null, new Type[] { propertyType }); // Определяем мутатор. ILGenerator setterIL = methodBuilderSetter.GetILGenerator(); // Получаем ссылку на генератор MSIL-инструкций для мутатора. setterIL.Emit(OpCodes.Ldarg_0); // Помещаем в стек вычислений нулевой аргумент. setterIL.Emit(OpCodes.Ldarg_1); // Помещаем в стек вычислений первый аргумент. setterIL.Emit(OpCodes.Stfld, fieldBuilder); // Помещаем в стек вычислений инструкцию о сохранении значения по ссылке поля. setterIL.Emit(OpCodes.Ret); // Помещаем в стек вычислений инструкцию о возврате из метода. propertyBuilder.SetGetMethod(methodBuilderAccessor); // Добавляем свойству аксессор. propertyBuilder.SetSetMethod(methodBuilderSetter); // Добавляем свойству мутатор. properties.Add(propertyName, propertyValue); // Сохраняем значения в словарь для дальнейшей передачи свойствам значений. } // Перебираем все поля нашего типа. foreach (FieldInfo fieldInfo in sourceType.GetFields()) { AmfMemberAttribute attribute = fieldInfo.GetAttribute <AmfMemberAttribute>(); // Получаем наш кастомный атрибут типа AmfMemberAttribute. if (attribute == null) { continue; // Если атрибут не указан - пропускаем поле. } string fieldName = attribute.Name ?? fieldInfo.Name; // Получаем имя поля. object fieldValue = fieldInfo.GetValue(sourceObject); // Получаем значение поля. Type fieldType = fieldInfo.FieldType; // Получаем метаданные типа поля. // Если у типа задан атрибут или это словарь... if (fieldInfo.FieldType.IsDefinedAttribute <AmfObjectAttribute>() || fieldType.IsDictionary()) { // Генерируем объект типа, заданного в атрибуте. fieldValue = fieldType.IsDictionary() ? GenerateType(fieldValue as IEnumerable <KeyValuePair <string, object> >) : GenerateType(fieldValue); fieldType = fieldValue.GetType(); // Обновляем тип поля. } typeBuilder.DefineField(fieldName, fieldType, FieldAttributes.Public); // Определяем новое поле. fields.Add(fieldName, fieldValue); // Сохраняем значения в словарь для дальнейшей передачи свойствам значений. } } else { // Перебираем все свойства нашего типа. foreach (PropertyInfo propertyInfo in sourceType.GetProperties()) { AmfMemberAttribute attribute = propertyInfo.GetAttribute <AmfMemberAttribute>(); // Получаем наш кастомный атрибут типа AmfMemberAttribute. if (attribute == null) { continue; // Если атрибут не указан - пропускаем свойство. } string propertyName = attribute.Name ?? propertyInfo.Name; // Получаем имя свойства. object propertyValue = propertyInfo.GetValue(sourceObject, null); // Получаем значение свойства. Type propertyType = propertyInfo.PropertyType; // Получаем метаданные типа свойства. AmfObjectAttribute propertyAttribute = propertyInfo.PropertyType.GetAttribute <AmfObjectAttribute>(); // Получаем атрибут у свойства. // Если у типа задан атрибут или это словарь... if (propertyAttribute != null || propertyType.IsDictionary()) { // Генерируем объект типа, заданного в атрибуте. propertyValue = propertyType.IsDictionary() ? GenerateType(propertyValue as IEnumerable <KeyValuePair <string, object> >) : GenerateType(propertyValue); propertyType = propertyValue.GetType(); // Обновляем тип свойства. } properties.Add(propertyName, propertyValue); // Сохраняем значения в словарь для дальнейшей передачи свойствам значений. } // Перебираем все поля нашего типа. foreach (FieldInfo fieldInfo in sourceType.GetFields()) { AmfMemberAttribute attribute = fieldInfo.GetAttribute <AmfMemberAttribute>(); // Получаем наш кастомный атрибут типа AmfMemberAttribute. if (attribute == null) { continue; // Если атрибут не указан - пропускаем поле. } string fieldName = attribute.Name ?? fieldInfo.Name; // Получаем имя поля. object fieldValue = fieldInfo.GetValue(sourceObject); // Получаем значение поля. Type fieldType = fieldInfo.FieldType; // Получаем метаданные типа поля. AmfObjectAttribute fieldAttribute = fieldInfo.FieldType.GetAttribute <AmfObjectAttribute>(); // Получаем атрибут у поля. // Если у типа задан атрибут или это словарь... if (fieldAttribute != null || fieldType.IsDictionary()) { // Генерируем объект типа, заданного в атрибуте. fieldValue = fieldType.IsDictionary() ? GenerateType(fieldValue as IEnumerable <KeyValuePair <string, object> >) : GenerateType(fieldValue); fieldType = fieldValue.GetType(); // Обновляем тип поля. } fields.Add(fieldName, fieldValue); // Сохраняем значения в словарь для дальнейшей передачи свойствам значений. } } object targetObject = Activator.CreateInstance(definedType ?? typeBuilder.CreateType()); // Создаём инстанс нашего динамического типа. // Раставляем значения всем свойствам объекта. foreach (KeyValuePair <string, object> property in properties) { targetObject.GetType().GetProperty(property.Key).SetValue(targetObject, property.Value, null); } // Раставляем значения всем полям объекта. foreach (KeyValuePair <string, object> field in fields) { targetObject.GetType().GetField(field.Key).SetValue(targetObject, field.Value); } return(targetObject); }
/// <summary> /// Десериализует буфер данных в объект AMF. /// </summary> /// <typeparam name="T">Тип десериализуемого объекта.</typeparam> /// <param name="sourceBuffer">Исходный буфер данных объекта.</param> /// <returns></returns> public static T DeserializeFromAmf <T>(this byte[] sourceBuffer) where T : class { using (MemoryStream memoryStream = new MemoryStream(sourceBuffer)) // Открываем поток для чтения данных из буфера. using (AMFDeserializer amfDeserializer = new AMFDeserializer(memoryStream)) // Инициализируем десериализатор для AMF. { AMFMessage amfMessage = amfDeserializer.ReadAMFMessage(); // Получем сообщение AMF. AMFBody amfBody = amfMessage.GetBodyAt(0); // Получаем body из сообщения AMF. object amfObject = amfBody.Content; // Получаем объект из body AMF. Type amfObjectType = amfObject.GetType(); // Получаем метаданные типа объекта AMF. // Формируем запрос на получение всей коллекции нужных нам типов с заданными атрибутами. IEnumerable <Type> types = from type in Assembly.GetExecutingAssembly().GetTypes() where Attribute.IsDefined(type, typeof(AmfObjectAttribute)) select type; Type currentType = null; // Определяем текущий тип объекта из нашей сборки. // Проходим по всем найденным типам с нашим атрибутом. foreach (Type type in types) { AmfObjectAttribute attribute = type.GetAttribute <AmfObjectAttribute>(); // Получаем наш атрибут. if (attribute == null || attribute.Name != amfObjectType.FullName) { continue; // Если в атрибуте задано другое имя - пропускаем итерацию. } currentType = type; // Иначе сохраняем текущий тип объекта. break; } if (currentType == null) { return(default(T)); // Если тип не найден - возвращаем null. } object targetObject = Activator.CreateInstance(currentType); // Создаём инстанс нашего типа. // Анализируем все свойства нашего класса. foreach (PropertyInfo propertyInfo in currentType.GetProperties()) { AmfMemberAttribute attribute = propertyInfo.GetAttribute <AmfMemberAttribute>(); // Получаем наш кастомный атрибут. if (attribute == null) { continue; // Если атрибут не задан - пропускаем. } propertyInfo.SetValue(targetObject, amfObjectType.GetProperty(attribute.Name).GetValue(amfObject, null), null); // Получаем значение свойства у десериализуемого объекта и сохраняем его в свойстве нашего объекта. } // Анализируем все поля нашего класса. foreach (FieldInfo fieldInfo in currentType.GetFields()) { AmfMemberAttribute attribute = fieldInfo.GetAttribute <AmfMemberAttribute>(); // Получаем наш кастомный атрибут. if (attribute == null) { continue; // Если атрибут не задан - пропускаем. } fieldInfo.SetValue(targetObject, amfObjectType.GetField(attribute.Name).GetValue(amfObject)); // Получаем значение поля у десериализуемого объекта и сохраняем его в поле нашего объекта. } return(targetObject as T); // Приводит к типу T и возвращает текущий объект. } }