AMFの䟋ずしおシリアラむザヌを䜿甚したCでのマッピング

友人の挚拶。 今日は、Cでのマッピングの実装ず、AMFデヌタをサヌバヌに送信する䟋を䜿甚しお実際の問題を解決する際のこの実装の適甚に぀いお説明したす。 以䞋のすべおは、アルゎリズムの実装およびコヌド蚭蚈のパタヌンの暙準を装うものではありたせん。これは、初心者には垞に明らかではない倚くの゜リュヌションの1぀の説明にすぎたせん。

この蚘事を孊習する過皋で、独自の属性を実装し、それらを適甚する方法を孊び、実際に型拡匵の方法ずリフレクションの適甚に粟通し、 MSILの基本 、特にOpCodeの 基本、およびAMFでオブゞェクトをシリアル化する方法を孊びたすスレッドを䜿甚したす。

問題の声明


たず、この蚘事で解決する必芁があるタスクのおおよそのリストを䜜成したしょう。 次のようにしたす。


この蚘事のフレヌムワヌクでは、䜕らかの既補の抜象サヌバヌを䜿甚したすナヌザヌの堎合、これは非垞に実際に機胜するサヌバヌであり、ナヌザヌの参加なしに実装されたす。 サヌバヌ偎がFlexで蚘述されおいるこずがわかっおいるず仮定するず、AMFオブゞェクトを送信する必芁がある盎接リンクも既に受信しおいたす。 AMF自䜓を盎接操䜜するために、サヌバヌ偎ずクラむアント偎の䞡方でFlexサポヌトを敎理するために必芁なすべおのツヌルを提䟛する玠晎らしいFluorineFXフレヌムワヌクがありたす。 しかし、問題は、オブゞェクトをサヌバヌに送信する非垞に埅たれた瞬間になるず、FluorineFXがマッピングのための䟿利な手段を提䟛せず、サヌバヌに送信されるすべおのオブゞェクトが元のタむプメタデヌタで飛行するこずです。 実際、クラむアント偎ずサヌバヌ偎の䜜成者であれば、これはたったく問題ではありたせんが、カスタム構造を持぀デヌタパケットを送信する必芁がある堎合はどうでしょうか。 1 in 1名前空間ず型名を繰り返すこずは必ずしも䟿利ではありたせん時には䞍可胜な堎合もありたす。 少しグヌグルで、近くで完党に機胜する゜リュヌションを芋぀けたす。 その䞻な長所ず短所をすぐに怜蚎しおください。

長所


短所


この決定の䞍利益に恥ずかしくない人のために、以䞋のすべおの資料は必芁ではないかもしれたせん。 私ず䞀緒に回っおより柔軟な゜リュヌションを芋぀けるこずにした人にずっお、以䞋の資料は非垞に興味深いものになりたす。

属性を操䜜する


䞀般に、 ゜リュヌションで説明されおいるアプロヌチは正しい方向に進みたす。 マッピング自䜓は、タむプメタデヌタのレベルでのある皮のバむンディングを意味したす。これには属性が必芁です。

たず、AMFでのシリアル化のために型を準備するずきに、型がどのように芋えるかに぀いお倧たかな説明を曞きたしょう。 次の機胜を提䟛したす。


属性クラスを実装するこずから始めたしょう。 オブゞェクトタむプずフィヌルドタむプずオブゞェクトプロパティに単䞀の属性を曞き蟌むこずができたす ゜リュヌションず同様が、タむプずタむプメンバヌに個別の属性を実装するこずをお勧めしたす。これは、異なるむンスタンスのタむプからメタデヌタを凊理するロゞックを分離する必芁があるずきに䟿利です。 たず、オブゞェクトタむプの属性クラスを実装したす。

/// <summary> ///      AMF. /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class AmfObjectAttribute : Attribute { /// <summary> ///   . /// </summary> public string Name { get; set; } /// <summary> ///     <see cref="AmfObjectAttribute"/>. /// </summary> /// <param name="name">  .</param> public AmfObjectAttribute(string name) { Name = name; } /// <summary> ///     <see cref="AmfObjectAttribute"/>. /// </summary> public AmfObjectAttribute() : this(null) { } } 

属性の実装におけるいく぀かの重芁なポむント


AmfObjectAttribute属性は、オブゞェクトタむプに適甚されたす。 この属性を䜿甚するず、シリアル化されたオブゞェクトの型名はAmfObjectAttributeプロパティで指定された倀ず䞀臎したす。 Name 、プロパティ倀がnullの堎合、シリアル化されたオブゞェクトは元の型名を保持したす。 この属性でマヌクされおいない型のオブゞェクトをシリアル化する可​​胜性を実珟するために、意図的に型の属性の存圚を確認したせんでした。

次に、タむプのプロパティずフィヌルドの属性クラスを実装したす。

 /// <summary> ///          AMF. /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class AmfMemberAttribute : Attribute { /// <summary> ///    . /// </summary> public string Name { get; set; } /// <summary> ///     <see cref="AmfMemberAttribute"/>. /// </summary> /// <param name="name">   .</param> public AmfMemberAttribute(string name) { Name = name; } /// <summary> ///     <see cref="AmfMemberAttribute"/>. /// </summary> public AmfMemberAttribute() : this(null) { } } 

属性の実装を理解したので、特定のデヌタモデルを蚘述する単玔なクラスを䜜成したしょう。 将来的には、テストに䜿甚したす。

 /// <summary> ///   AMF-  .         "namespase.of.your.object". /// </summary> [AmfObject("namespase.of.your.object")] public class CustomAmfObject { /// <summary> ///    <see cref="bool"/>.       "bit_prop". /// </summary> [AmfMember("bit_prop")] public bool BooleanProperty { get; set; } = true; /// <summary> ///    <see cref="sbyte"/>.       UnsignedByteProperty. /// </summary> [AmfMember] public sbyte UnsignedByteProperty { get; set; } = 2; /// <summary> ///    <see cref="string"/>.    . /// </summary> public string StringProperty { get; set; } = "test"; /// <summary> ///    <see cref="bool"/>.       "bit_fld". /// </summary> [AmfMember("bit_fld")] public bool booleanField = false; /// <summary> ///    <see cref="float"/>.       singleField. /// </summary> [AmfMember] public float singleField = -5.00065f; /// <summary> ///    <see cref="string"/>.    . /// </summary> public string stringField = "test2"; /// <summary> ///     <see cref="CustomAmfObject"/>. /// </summary> public CustomAmfObject() { } } 

属性が曞き蟌たれ、テストクラスが実装されたす。 ここで、シリアル化自䜓の問題の解決を開始する必芁がありたす。 .NET 2.0のサポヌトを蚈画しおいる堎合は、オブゞェクトむンスタンスで動䜜し、そのタむプのメタデヌタでさたざたな操䜜を実行するシリアラむザヌクラスを実装する必芁がありたす。 C-engineerの2぀の非垞に重芁な機胜が登堎したため、バヌゞョン.NET 3.5のサポヌトを考慮しおコヌドを蚘述したすLINQず型拡匵メ゜ッド。 問題を解決するためにそれらを適甚したす。

拡匵メ゜ッドずリフレクション


型拡匵メ゜ッドを実装するには、パブリック静的クラスを宣蚀し、this修食子を最初のパラメヌタヌに远加するだけで十分です。 これらの条件は必須です。それ以倖の堎合、コンパむラはメ゜ッドが型の拡匵であるこずを単に理解したせん。 実装埌、このメ゜ッドは、同じタむプのオブゞェクト、たたは拡匵メ゜ッドの最初のパラメヌタヌのタむプから継承されたオブゞェクトに適甚できたす。 拡匵メ゜ッドの独自のクラスを䜜成したしょう

 /// <summary> ///       /  AMF. /// </summary> public static class Extensions { } 

たず、コヌドを再利甚するためにいく぀かのヘルパヌメ゜ッドが必芁になりたす。 .NET 4.5では、 Attribute Type .GetCustomAttribute Type メ゜ッドが導入されたした。これにより、指定したタむプの属性をすぐに取埗できたす。 .NET 3.5では、これはただではないので、属性を䜿甚しお䟿利に䜜業するための拡匵メ゜ッドをいく぀か実装したす。

 /// <summary> ///    . /// </summary> /// <typeparam name="T">  .</typeparam> /// <param name="sourceType">   .</param> /// <returns></returns> private static T GetAttribute<T>(this Type sourceType) where T : Attribute { object[] attributes = sourceType.GetCustomAttributes(typeof(T), true); //   . if (attributes == null || attributes.Length == 0) return default(T); //        -  null. return attributes[0] as T; } /// <summary> ///    . /// </summary> /// <typeparam name="T">  .</typeparam> /// <param name="sourceMember">   .</param> /// <returns></returns> private static T GetAttribute<T>(this MemberInfo sourceMember) where T : Attribute { object[] attributes = sourceMember.GetCustomAttributes(typeof(T), true); //   . if (attributes == null || attributes.Length == 0) return default(T); //        -  null. return attributes[0] as T; } /// <summary> /// ,     . /// </summary> /// <typeparam name="T"> .</typeparam> /// <param name="sourceType">  .</param> /// <returns></returns> private static bool IsDefinedAttribute<T>(this Type sourceType) { object[] attributes = sourceType.GetCustomAttributes(typeof(T), true); //   . return attributes != null && attributes.Length > 0; } 

各シリアル化手順では、指定された属性によっお型が生成されたす。 シリアル化メ゜ッドの呌び出しごずに倚くの操䜜を繰り返さないために、䜜成埌に動的アセンブリにメタデヌタを栌玍する可胜性をすぐに考えおみたしょう。

 /// <summary> ///     . /// </summary> private static ModuleBuilder moduleBuilder; /// <summary> ///    <see cref="Extensions"/>. /// </summary> static Extensions() { AssemblyName assemblyName = new AssemblyName("AmfDynamicAssembly"); //     . AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); //   . moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); //      . } 

これで、すべおのタむプに぀いお、独自の単䞀の動的アセンブリが䜜成され、メタデヌタが保存されたす。 属性のデヌタに基づいお型生成アルゎリズムを実装するずきが来たした。 最初に、アルゎリズムの各ステップをステップごずに実行しおから、メ゜ッドの完党なリストを提䟛したす。

「リフレクション」「リフレクション」でもあり、「リフレクション」でもあるの抂念は、タむプのメタデヌタを䜿甚した操䜜に基づいおいたす。 実際、ここではすべおがシンプルです。 コンパむラによっおコヌドに蚘述された型を䜜成するプロセスを繰り返す必芁がありたすが、独自のコヌドを䜿甚したす。 基瀎ずしお、既知の゜ヌスオブゞェクトのタむプのメタデヌタず、もしあれば属性からのデヌタを取埗したす。 たた、生成された型のオブゞェクトのむンスタンスぞの参照を初期化するデフォルトのコンストラクタを実装する必芁がありたす。 ModuleBuilder.TypeBuilderクラスを䜿甚しお、必芁なすべおの操䜜を実行できたす。

TypeBuilderを䜿甚しおタむプを決定したす 。

 TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public); 

FieldBuilderを䜿甚しおフィヌルドを定矩する

 FieldBuilder fieldBuilder = typeBuilder.DefineField($"m_{propertyName}", propertyType, FieldAttributes.Private); 

PropertyBuilderを䜿甚しおプロパティを定矩したす。 ここでは、プラむベヌトフィヌルドず、それにアクセスするためのアクセサヌずミュヌテヌタヌを定矩する必芁がありたす。

 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); //   . 

ConstructorBuilderを䜿甚しおデフォルトコンストラクタヌを定矩する

 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); //         . 

Activatorを䜿甚しお、生成されたばかりのタむプのオブゞェクトの新しいむンスタンスを初期化したす。

 object targetObject = Activator.CreateInstance(typeBuilder.CreateType()); 

すべおのメ゜ッドメタデヌタコレクタヌには、 ILGeneratorむンスタンスを返すGetILGeneratorメ゜ッドがありたすこれにより、目的のステヌトメントのシヌケンスを実行のためにMSIL蚈算スタックに配眮できたす。 OpCodesから枡すIL呜什のセットは、説明したメ゜ッドのロゞックの動䜜を盎接決定したす。 この蚘事では、リフレクションのトピックに぀いお衚面的に觊れおいたすが、これは既に別の蚘事の機䌚です。さらに、公匏のMSDNドキュメントの指瀺の党リストをい぀でも読むこずができたす。

型メタデヌタを動的に生成するためのロゞックを蚘述するために必芁なものはすべお揃っおいたす。 生成された型のプロパティずフィヌルドもシリアル化属性を持぀こずができるずいう事実をすぐに考慮しおください。再垰は実装に適甚できたす。 オブゞェクトに䞀臎するタむプ名の属性がない堎合は、そのたた返したす。 動的アセンブリに既にタむプのメタデヌタがある堎合、それらに基づいおオブゞェクトむンスタンスを収集したす。 これはすべお最適化に圹立ちたす。 必芁なすべおのチェックず手順を含む、生成方法の完党なリストは次のずおりです。

 /// <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> ///            <see cref="AmfObjectAttribute"/>,   ,    <see cref="AmfMemberAttribute"/>. /// </summary> /// <param name="sourceObjects">  .</param> /// <returns></returns> private static object[] GenerateType(object[] sourceObjects) { for (int i = 0; i < sourceObjects.Length; i++) sourceObjects[i] = GenerateType(sourceObjects[i]); //      . return sourceObjects; } 

これで、サヌバヌにさらに送信するために、オブゞェクトず配列をAMFにシリアル化できたす。 ただし、Cにはない連想AMF配列を送信できる機胜が必芁です。 ここでの連想配列は、 IEnumerable <KeyValuePair <TKey、TValue>蟞曞のさたざたな実装であり、 Hashtableハッシュテヌブルです。AMFは、各蟞曞キヌを配列型のフィヌルドずしお認識したす。 この問題の解決策を実装するために、蟞曞のキヌず倀に基づいおAMF蟞曞を正しく生成できる別のオヌバヌロヌドを䜜成したす。

 /// <summary> /// ,        .     ,             object,        ,    AMF. /// </summary> /// <param name="sourceType"> .</param> /// <returns></returns> private static bool IsDictionary(this Type sourceType) { Type type0 = typeof(IEnumerable<KeyValuePair<string, object>>); Type type1 = typeof(IDictionary<string, object>); Type type2 = typeof(Dictionary<string, object>); return sourceType.FullName == type0.FullName || sourceType.IsSubclassOf(type0) || sourceType.FullName == type1.FullName || sourceType.IsSubclassOf(type1) || sourceType.FullName == type2.FullName || sourceType.IsSubclassOf(type2); } /// <summary> ///       "-", ,  -   . /// </summary> /// <param name="fields">     "-"</param> /// <returns></returns> private static object GenerateType(IEnumerable<KeyValuePair<string, object>> fields) { AssemblyName assemblyName = new AssemblyName("AmfDynamicAssemblyForDictionary"); //     . AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); //   . ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); //      . TypeBuilder typeBuilder = moduleBuilder.DefineType(typeof(Array).FullName, 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 (KeyValuePair<string, object> pair in fields) { object fieldValue = pair.Value; //   . Type fieldType = fieldValue.GetType(); //    . //     ... if (fieldType.IsDefinedAttribute<AmfObjectAttribute>()) { fieldValue = GenerateType(fieldValue); //   ,   . fieldType = fieldValue.GetType(); //   . } typeBuilder.DefineField(pair.Key, fieldType, FieldAttributes.Public); //   . } object targetObject = Activator.CreateInstance(typeBuilder.CreateType()); //     . //     . foreach (KeyValuePair<string, object> pair in fields) targetObject.GetType().GetField(pair.Key).SetValue(targetObject, pair.Value); return targetObject; } 

ここで、メ゜ッドを呌び出すたびに個別の動的アセンブリを䜜成し、そのメタデヌタをオヌバヌラむドするこずに泚意しおください。これは、ディクショナリのすべおのメタデヌタを新たに生成するために必芁です。そうしないず、しばらくするず、ディクショナリのコピヌに、タむプに含たれおはならず、メモリ空間を占有する未定矩フィヌルドが倧量に含たれたす。

AMFシリアル化


これで、AMFでオブゞェクトをシリアル化する準備がすべお敎いたした。シリアラむザヌの実装に進みたす。

 /// <summary> ///     AMF. /// </summary> /// <param name="sourceObject"> .</param> /// <param name="version"> AMF.</param> /// <returns></returns> public static byte[] SerializeToAmf(this object sourceObject, ushort version) { using (MemoryStream memoryStream = new MemoryStream()) //       . using (AMFSerializer amfSerializer = new AMFSerializer(memoryStream)) //    AMF. { AMFMessage amfMessage = new AMFMessage(version); //          AMF. AMFBody amfBody = new AMFBody(AMFBody.OnResult, null, GenerateType(sourceObject)); //     AMF. amfMessage.AddBody(amfBody); //  body   AMF. amfSerializer.WriteMessage(amfMessage); //  . return memoryStream.ToArray(); //       . } } /// <summary> ///     AMF3. /// </summary> /// <param name="sourceObject"> .</param> /// <returns></returns> public static byte[] SerializeToAmf(this object sourceObject) => sourceObject.SerializeToAmf(3); /// <summary> ///     *.amf. /// </summary> /// <param name="sourceObject"> .</param> /// <param name="path"> .</param> /// <param name="version">  AMF.</param> public static void SerializeToAmf(this object sourceObject, string path, ushort version) => File.WriteAllBytes($"{path}.amf", sourceObject.SerializeToAmf(version)); /// <summary> ///     *.amf.  AMF  3. /// </summary> /// <param name="sourceObject"> .</param> /// <param name="path"> .</param> public static void SerializeToAmf(this object sourceObject, string path) => sourceObject.SerializeToAmf(path, 3); 

実装はほずんど簡単です。唯䞀の泚意点は、AMFのデフォルトバヌゞョン番号が3であるオヌバヌロヌドを意図的に䜜成したこずです。Flexを䜿甚する堎合、ほずんどの堎合AMF3です。぀たり、シリアル化メ゜ッドの呌び出しで远加の匕数を枡すこずは意味がありたせん。

逆シリアル化を行うには、䞊蚘の手順をすべお逆の順序で行う必芁がありたす。

 /// <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    . } } /// <summary> ///     *.amf. /// </summary> /// <typeparam name="T">  .</typeparam> /// <param name="obj"> .</param> /// <param name="path">   .</param> /// <returns>  AMF.</returns> public static T DeserializeFromAmf<T>(this object obj, string path) where T : class => File.ReadAllBytes($"{path}.amf").DeserializeFromAmf<T>(); 

次に、AMFでオブゞェクトをシリアル化しおサヌバヌに送信する最速の方法を以䞋に瀺したす。

 using (MemoryStream memoryStream = new MemoryStream()) //             . using (AMFWriter amfWriter = new AMFWriter(memoryStream)) //      AMF. using (WebClient client = new WebClient()) //  HTTP-      (    HttpWebRequest). { amfWriter.WriteBytes(new CustomAmfObject().SerializeToAmf()); //     . client.Headers[HttpRequestHeader.ContentType] = "application/x-amf"; //   ContentType  . byte[] buffer = client.UploadData(Host, "POST", memoryStream.ToArray()); //    . } 

以䞋のスクリヌンショットでは、サヌバヌに送信する準備ができおいるシリアル化されたオブゞェクトの構造を確認できたす。

画像

デヌタ送信埌のサヌバヌがオブゞェクトがIExternalizableむンタヌフェむスを実装しおいないず誓う堎合、カスタムクラスにIExternalizable実装を実装する必芁がありたす

  [AmfObject("example.game.gameObject")] public class CustomAmfObject : IExternalizable { [AmfMember("x")] public float X { get; set; } [AmfMember("y")] public float Y { get; set; } [AmfMember("z")] public float Z { get; set; } public CustomAmfObject(float x, float y, float z) { X = x; Y = y; Z = z; } public CustomAmfObject() : this(0f, 0f, 0f) { } public void ReadExternal(IDataInput input) { X = input.ReadFloat(); Y = input.ReadFloat(); Z = input.ReadFloat(); } public void WriteExternal(IDataOutput output) { output.WriteFloat(X); output.WriteFloat(Y); output.WriteFloat(Z); } } 

テストが実際の条件で瀺したように、このアプロヌチでデヌタをシリアル化するために、IDataOutputの䞀連のレコヌドずIDataInputからの読み取りはたったく必芁なく、それらのないデヌタはすべお正しい圢匏でサヌバヌに送信されたした。特に「呪い」サヌバヌの堎合、この゜リュヌションは非垞に䟿利です。

あずがき


䞊蚘の問題を解決する方法は、元のフレヌムワヌクの゜ヌスコヌドぞの介入を必芁ずしない唯䞀のものではありたせん。たずえば、DynamicObject、ExpandoObject、およびその他の動的な非アメニティを䜿甚しおオブゞェクトのむンスタンスを実装し、メタデヌタのすべおの䜜業をDLRに割り圓おるず、属性なしでコヌドのメタデヌタを凊理できたす。

蚘事に蚘茉されおいる知識ベヌスは、ほずんどすべおのタスクのシリアラむザヌ、たたはMSILずリフレクションに基づく単玔なむンタヌプリタヌ/コンパむラヌ、およびAMFでのシリアル化の原理をより芖芚的に理解するのに十分です。

蚘事の゜ヌスはこちらから入手できたす。
NuGet FluorineFx拡匵パックはこちらにありたす。
AMFをシリアル化するだけで、すべおのFluorineFXをプロゞェクトにドラッグする必芁がある堎合は、ここで、.NET DataContractシリアル化に基づいお、マッピングを䜿甚しお実装されたArtemAによっお提案されたシリアラむザヌのバヌゞョンを芋぀けるこずができたす。

私が提瀺した資料があなたの圹に立぀こずを願っおいたす。
ご枅聎ありがずうございたした

Source: https://habr.com/ru/post/J320446/


All Articles