0

I am trying to create a utility function for serializing objects, Normally, serialization would happen as follows:

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

However, I want to be able to do the following:

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

I do this with the following two methods:

This one for deserializing the value:

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

and this one for serializing the value:

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

I am getting an exception when trying to get the value from the Body.Member when it is a property (when it is a field, it works fine). How can I get this?

Other questions - 1) Are there any issues with the approach that I am taking? 2) Is there a better way perhaps to go about this whole thing? 3) When will the Body.Member be a FieldInfo and when will it be a PropertyInfo?

This is sort of an extension of my previous question Here

Community
  • 1
  • 1
William
  • 3,335
  • 9
  • 42
  • 74
  • "I am getting an exception when trying" What's the exception? – Tim S. Jun 07 '12 at 16:54
  • I just broke it down a little bit and the exception is happening when I caste the object value to T - so it is actually letting me get the value from the PropertyInfo, but how can I caste it into the correct return type? – William Jun 07 '12 at 18:41

1 Answers1

1

Does the AddValue method have to be so complex? I assume the following would also work. Instead of using reflection, it compiles and evaluates the lambda expression in order to get the value.

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

Edit: To cater for performance-sensitive situations, I usually define the extension method with two overloads:

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

This way, you may call either of the following options:

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

The latter overload has a degree of redundancy in its parameters, but would address your performance concern (being actually faster than your reflection-based implementation) whilst still preserving refactor-safety.

Douglas
  • 53,759
  • 13
  • 140
  • 188
  • what's the performance of this method? I'm not SUPER performance sensitive, but if you're compiling each of these calls and I do it 10,000 times in the program, it will be an issue if this method is a lot slower ... – William Jun 07 '12 at 18:43