【问题标题】:Method Extension for strongly-typed Serialization of properties and fields属性和字段的强类型序列化的方法扩展
【发布时间】:2012-06-08 02:48:19
【问题描述】:

我正在尝试创建一个用于序列化对象的实用程序函数, 通常,序列化会发生如下:

[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);
    }
}

我使用以下两种方法:

这个用于反序列化值:

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?

这是我之前的问题Here的延伸

【问题讨论】:

  • “我在尝试时遇到异常”是什么异常?
  • 我只是把它分解了一点,当我将对象值强制转换为 T 时发生异常 - 所以它实际上是让我从 PropertyInfo 中获取值,但我怎样才能将它强制转换为正确的返回类型?

标签: c# serialization reflection


【解决方案1】:

AddValue 方法有必要这么复杂吗?我认为以下内容也可以。它不使用反射,而是编译和评估 lambda 表达式以获取值。

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));
}

编辑:为了满足性能敏感的情况,我通常用两个重载定义扩展方法:

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

后一种重载在其参数中有一定程度的冗余,但会解决您的性能问题(实际上比基于反射的实现更快),同时仍然保持重构安全。

【讨论】:

  • 这种方法的性能如何?我对性能不是很敏感,但是如果你要编译这些调用中的每一个并且我在程序中执行 10,000 次,那么如果这个方法慢得多,那将是一个问题......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多