我正在尝试创建一个用于序列化对象的实用程序函数, 通常,序列化将发生如下:
[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
的扩展答案 0 :(得分: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);
后一种重载在其参数中具有一定程度的冗余,但会解决您的性能问题(实际上比基于反射的实现更快),同时仍然保留了重构安全性。