0

オブジェクトをシリアル化するためのユーティリティ関数を作成しようとしています。通常、シリアル化は次のように行われます。

[Serializable]
public CoolCat : ISerializable
{
    public string Name;

    public void CoolCar(SerializationInfo info, StreamingContext context)
    {
        Name = (string)info.GetValue("Name", typeof(string));
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Name", Name);
    }
}

ただし、次のことができるようにしたいです。

[Serializable]
public CoolCat : ISerializable
{
    public string Name;

    public void CoolCar(SerializationInfo info, StreamingContext context)
    {
        Name = info.GetValue<string>(() => Name);
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue<string>(() => Name);
    }
}

私は次の 2 つの方法でこれを行います。

これは値を逆シリアル化するためのものです:

public static T GetValue<T>(this SerializationInfo Source, Expression<Func<T>> MemberExpression)
{
    string Name = ((MemberExpression)MemberExpression.Body).Member.Name;
    return (T)Source.GetValue(Name, typeof(T));
}

そしてこれは値をシリアライズするためのものです:

public static void AddValue<T>(this SerializationInfo Source, Expression<Func<T>> MemberExpression)
{
    MemberExpression Body = MemberExpression.Body as MemberExpression;

    if (Body == null)
    {
        UnaryExpression UnaryBody = MemberExpression.Body as UnaryExpression;

        if (UnaryBody != null)
        {
            Body = UnaryBody.Operand as MemberExpression;
        }
        else
        {
            throw new ArgumentException("Expression is not a MemberExpression", "MemberExpression");
        }
    }

    string Name = Body.Member.Name;

    if (Body.Member is FieldInfo)
    {
        T Value = (T)((FieldInfo)Body.Member).GetValue(((ConstantExpression)Body.Expression).Value);
        Source.AddValue(Name, Value, typeof(T));
    }
    else if (Body.Member is PropertyInfo)
    {
        T Value = (T)((PropertyInfo)Body.Member).GetValue(((ConstantExpression)Body.Expression, null);
        Source.AddValue(Name, Value, typeof(T));
    }
    else
    {
        throw new ArgumentException("Expression must refer to only a Field or a Property", "MemberExpression");
    }
}

Body.Member がプロパティの場合に値を取得しようとすると、例外が発生します (フィールドの場合は正常に動作します)。どうすればこれを入手できますか?

その他の質問 - 1) 私が取っているアプローチに問題はありますか? 2) おそらくこのすべてを行うためのより良い方法はありますか? 3) Body.Member はいつ FieldInfo になり、いつ PropertyInfo になりますか?

これは私の以前の質問の延長のようなものです

4

1 に答える 1

1

AddValueメソッドはそれほど複雑にする必要がありますか? 以下もうまくいくと思います。リフレクションを使用する代わりに、値を取得するためにラムダ式をコンパイルおよび評価します。

public static void AddValue<T>(
    this SerializationInfo source, 
    Expression<Func<T>> memberExpression)
{
    MemberExpression body = memberExpression.Body as MemberExpression;
    string name = body.Member.Name;
    Func<T> valFunc = memberExpression.Compile();
    T val = valFunc();

    source.AddValue(name, val, typeof(T));
}

編集:パフォーマンスが重要な状況に対応するために、通常、2 つのオーバーロードを使用して拡張メソッドを定義します。

public static void AddValue<T>(
    this SerializationInfo source,
    Expression<Func<T>> memberExpression)
{
    Func<T> valFunc = memberExpression.Compile();
    T val = valFunc();

    source.AddValue(val, memberExpression);
}

public static void AddValue<T>(
    this SerializationInfo source,
    T val,
    Expression<Func<T>> memberExpression)
{
    MemberExpression body = memberExpression.Body as MemberExpression;
    string name = body.Member.Name;

    source.AddValue(name, val, typeof(T));
}

このようにして、次のオプションのいずれかを呼び出すことができます。

// Inefficient, since it requires compilation of lambda expression:
info.AddValue<string>(() => Name);

// Faster, but requires you to specify two parameters.
info.AddValue<string>(Name, () => Name);

後者のオーバーロードには、パラメーターにある程度の冗長性がありますが、リファクタリングの安全性を維持しながら、パフォーマンスの問題 (実際にはリフレクションベースの実装よりも高速です) に対処します。

于 2012-06-07T16:57:46.993 に答える