0

リフレクションを使用してオブジェクトのパブリックまたはプライベート フィールドを再帰的にクロールして XML として保存し、後で再構築する XML シリアライザーを作成しています。ポインタと、そうすることで、マネージド/アンマネージド境界を越え、(ありがたいことに) メモリを混乱させる代わりにクラッシュしました。この問題の解決策を見つける必要がありますが、アンマネージ コードのバックグラウンドがないため、やや迷っています。

ポインターまたは参照の値はメモリアドレスであるため、ポインターまたは参照を実際にそのままシリアル化することはできず、ポインターを再インスタンス化するときに正しいオブジェクトがそのアドレスにあるとは期待できません。 XML。私が理解しているように、この問題の原因となるオブジェクトを検出して無視するか、ポイントされているオブジェクトを見つけてシリアル化し、逆シリアル化時にそのオブジェクトを逆シリアル化し、そのオブジェクトの場所にポインターを向ける必要があります。しかし、どうすればよいかわかりません。私の最初の推測では、Type.IsPointer でフィルタリングしても問題は解決しなかったようです。私が求めていることはできますか?より良い解決策はありますか?戦略的なアンマネージ コードでこれを行うことはできますか?

コンテキスト: 通常の XmlSerializer ではできない型 (IDictionary、循環参照を持つ型など) をシリアル化できるシリアライザーを作成しています。私のシリアライザーは属性と ISerializeable または IXMLSerializeable の実装を無視します。オブジェクトのすべてのフィールドをシリアル化するために、いくつかのルールを再帰的にやみくもに使用します。動作しますが、一部のオブジェクトでネイティブ/マネージド境界にぶつかっています。オブジェクトが常に変更されており、オブジェクトのバージョンの競合をバイナリ シリアル化で解決する方法がわからないため、バイナリ シリアル化を使用していません。

編集:これは、「System.Globalization.TextInfo」クラスを再インスタンス化しようとするとクラッシュするコードです。これは、DataTable のどこかに深く埋め込まれたカルチャ オブジェクトの一部であると思います。これらの関数は、初期型パラメーターが再インスタンス化されるまで (常に ReInstantiateValueInstance から開始して) 相互に再帰的に呼び出します。

System.Globalization.TextInfo(CultureInfo) を再インスタンス化すると、「bestCtor.Invoke(parameters.ToArray())」でマネージド/ネイティブ境界例外がスローされます。

    protected object ReCreateTypeWithParameters(Type t)
    {
        if (t.ToString() == "System.Type") return typeof(object); //we dont know the type of type

        var construct = StoreUtilities.GetConstructors(t); //gets any and all constructors for an object

        if (construct != null && construct.Count > 0)
        {
            var leastParams = (from c in construct
                               select c.GetParameters().Count()).Min();

            var bestCtor = (from c in construct
                            where c.GetParameters().Count() == leastParams
                            select c).FirstOrDefault(); //the best constructor has the least parameters - less can go wrong

            if (bestCtor != null)
            {
                List<object> parameters = new List<object>();

                foreach (var param in bestCtor.GetParameters())
                {
                    parameters.Add(ReInstantiateValueInstance(param.ParameterType));
                }

                return bestCtor.Invoke(parameters.ToArray()); //pointer types go boom here.
            }
        }           

        return null;
    }

    protected virtual object ReInstantiateValueInstance(Type t)
    {
        try
        {       
            if (t.ToString() == "System.Type") //we don't know the Type of Type
            {
                return typeof(object);
            }
            var construct = StoreUtilities.GetConstructors(t, true); //gets an object's parameterless constructors

            if (construct == null && t.IsGenericType) //no constructor, it's generic
            {
                object generic = ReCreateGenericType(t);

                if (generic == null) //if the generic type had no constructor, we use the activator.
                {
                    return Activator.CreateInstance(t);
                }
                else
                {
                    return generic;
                }
            }

            if (construct == null || construct.Count() == 0) //we have no constuctor. Try and make a placeholder object anyways.
            {
               return ReCreateTypeWithParameters(t);
            }

            object o = construct.First().Invoke(null);
            return o;
        }
        catch
        {
            return null;
        }
    }

    protected object ReCreateGenericType(Type t)
    {
        try
        {
            if (Type.IsGenericType != true) return null;
            var construct = StoreUtilities.GetConstructors(Type, false);

            if (construct != null && construct.Count() > 0)
            {
                construct = construct.OrderBy(i => i.GetParameters().Count()).ToList();
                var tParams = construct[0].GetParameters();
                List<object> paramList = new List<object>();

                foreach (var p in tParams)
                {
                    if (StoreUtilities.CanStoreAsString(p.ParameterType) == true)
                    {
                        object o = Activator.CreateInstance(p.ParameterType);
                        paramList.Add(o);
                    }
                    else
                    {
                        paramList.Add(ReInstantiateValueInstance(p.ParameterType));
                    }
                }

                return construct[0].Invoke(paramList.ToArray());
            }
            else
            {
                return Activator.CreateInstance(t);
            }
        }
        catch
        {
            return null;
        }
    }
4

2 に答える 2

0

述べたように、この問題を解決することはできません。オブジェクトに「管理されていないデータ」への参照が含まれているかどうかを知ることはできません。Int64アンマネージメモリへStringのポインタを、、、などに格納している可能性がありますbyte[]。開発者はあらゆる種類のトリックを使用します。

どういうわけかこれを検出し、管理されていない参照を持つオブジェクトを「無視」できる場合は、データを逆シリアル化しようとすると不完全なオブジェクトになってしまうため、ゲームに負けています。

これを解決する唯一の方法は、シリアル化するオブジェクトの助けを借りることです。オプションのインターフェースを介して、リフレクションまたは属性を介して検出できないデータのシリアル化/逆シリアル化を支援するために実装できます。

このヘルプなしで普遍的に機能できる唯一のシリアライザーは、シリアル化するオブジェクトが理由でPOCOである必要がある傾向があります...

于 2013-03-07T04:03:44.050 に答える
0

マネージド/アンマネージドがこれにどのように関連しているかはわかりませんが、これを行うための基本的な方法は、内部参照用のシリアル化されたデータに、ある種の抽象化、ラベルを含めることです。シリアル化中に、ラベルとともに辞書などへの参照を追加して、オブジェクトを1回だけシリアル化するようにします。

デシリアライズはこのプロセスを反映し、特定のラベルを持つ参照を1回だけ作成し、残りの参照はラベルによって既存のインスタンスを検索します。

于 2013-03-07T01:40:02.497 に答える