[英]Get PropertyInfo of a C# auto property given the backing field
I am implementing a custom IFormatter to serialize objects into a custom format that is required by our legacy systems. 我正在实现一个自定义IFormatter,将对象序列化为我们的遗留系统所需的自定义格式。
If I declare a C# auto property: 如果我声明一个C#auto属性:
[StringLength(15)]
public MyProperty { get; set; }
And then in my custom serialize method I get the serialized fields via: 然后在我的自定义序列化方法中,我通过以下方式获取序列化字段:
MemberInfo[] members =
FormatterServices.GetSerializableMembers(graph.GetType(), Context);
How can I access the StringLength attribute that decorates the auto Property? 如何访问装饰auto属性的StringLength属性?
I am currently getting the property info by taking advantage of the <PropertyName>k_backingfield
naming convention. 我目前通过利用
<PropertyName>k_backingfield
命名约定获取属性信息。 I'd rather not rely on this as it seems to be a specific detail of the C# compiler implementation. 我宁愿不依赖它,因为它似乎是C#编译器实现的具体细节。 Is there a better way?
有没有更好的办法?
The better way would be to stop relying on private fields for serialization (as FormatterServices.GetSerializableMembers
returns) and only use public Properties instead. 更好的方法是停止依赖私有字段进行序列化(如
FormatterServices.GetSerializableMembers
返回),而只使用公共属性。
It is a LOT cleaner and works in this specific case. 它是一个很多清洁工,在这种特殊情况下工作。
But due to legacy code you might want to continue to use FormatterServices.GetSerializableMembers
and in this case, no there is no other options for you other than using the naming convention (Or a little bit of IL analysis) and it may break at each new compiler release. 但由于遗留代码,您可能希望继续使用
FormatterServices.GetSerializableMembers
,在这种情况下,除了使用命名约定(或者一点IL分析)之外,没有其他选项,并且它可能会在每个新的编译器发布。
Just for fun here is some code to do a little bit of IL analysis (It lack ignoring NOOPs and other niceties but should work with most current compilers. If you really adopt such a solution check the Cecil library (written by Jb Evain ) as it contains a full decompiler and it's better than doing it by hand. 只是为了好玩,这里有一些代码可以做一些IL分析(它缺少忽略NOOP和其他细节,但应该适用于大多数当前的编译器。如果你真的采用这样的解决方案,请检查Cecil库(由Jb Evain编写),因为它包含一个完整的反编译器,它比手工完成更好。
It's usage is like this : 它的用法是这样的:
void Main()
{
var members = FormatterServices.GetSerializableMembers(typeof(Foo));
var propertyFieldAssoc = new PropertyFieldAssociation(typeof(Foo));
foreach(var member in members)
{
var attributes = member.GetCustomAttributes(false).ToList();
if (member is FieldInfo)
{
var property = propertyFieldAssoc.GetProperty((FieldInfo)member);
if (property != null)
{
attributes.AddRange(property.GetCustomAttributes(false));
}
}
Console.WriteLine(member.Name);
foreach(var attribute in attributes)
{
Console.WriteLine(" * {0}", attribute.GetType().FullName);
}
Console.WriteLine();
}
}
And the code : 和代码:
class PropertyFieldAssociation
{
const byte LDARG_0 = 0x2;
const byte LDARG_1 = 0x3;
const byte STFLD = 0x7D;
const byte LDFLD = 0x7B;
const byte RET = 0x2A;
static FieldInfo GetFieldFromGetMethod(MethodInfo getMethod)
{
if (getMethod == null) throw new ArgumentNullException("getMethod");
var body = getMethod.GetMethodBody();
if (body.LocalVariables.Count > 0) return null;
var il = body.GetILAsByteArray();
if (il.Length != 7) return null;
var ilStream = new BinaryReader(new MemoryStream(il));
if (ilStream.ReadByte() != LDARG_0) return null;
if (ilStream.ReadByte() != LDFLD) return null;
var fieldToken = ilStream.ReadInt32();
var field = getMethod.Module.ResolveField(fieldToken);
if (ilStream.ReadByte() != RET) return null;
return field;
}
static FieldInfo GetFieldFromSetMethod(MethodInfo setMethod)
{
if (setMethod == null) throw new ArgumentNullException("setMethod");
var body = setMethod.GetMethodBody();
if (body.LocalVariables.Count > 0) return null;
var il = body.GetILAsByteArray();
if (il.Length != 8) return null;
var ilStream = new BinaryReader(new MemoryStream(il));
if (ilStream.ReadByte() != LDARG_0) return null;
if (ilStream.ReadByte() != LDARG_1) return null;
if (ilStream.ReadByte() != STFLD) return null;
var fieldToken = ilStream.ReadInt32();
var field = setMethod.Module.ResolveField(fieldToken);
if (ilStream.ReadByte() != RET) return null;
return field;
}
public static FieldInfo GetFieldFromProperty(PropertyInfo property)
{
if (property == null) throw new ArgumentNullException("property");
var get = GetFieldFromGetMethod(property.GetGetMethod());
var set = GetFieldFromSetMethod(property.GetSetMethod());
if (get == set) return get;
else return null;
}
Dictionary<PropertyInfo, FieldInfo> propertyToField = new Dictionary<PropertyInfo, FieldInfo>();
Dictionary<FieldInfo, PropertyInfo> fieldToProperty = new Dictionary<FieldInfo, PropertyInfo>();
public PropertyInfo GetProperty(FieldInfo field)
{
PropertyInfo result;
fieldToProperty.TryGetValue(field, out result);
return result;
}
public FieldInfo GetField(PropertyInfo property)
{
FieldInfo result;
propertyToField.TryGetValue(property, out result);
return result;
}
public PropertyFieldAssociation(Type t)
{
if (t == null) throw new ArgumentNullException("t");
foreach(var property in t.GetProperties())
{
Add(property);
}
}
void Add(PropertyInfo property)
{
if (property == null) throw new ArgumentNullException("property");
var field = GetFieldFromProperty(property);
if (field == null) return;
propertyToField.Add(property, field);
fieldToProperty.Add(field, property);
}
}
class StringLengthAttribute : Attribute
{
public StringLengthAttribute(int l)
{
}
}
[Serializable]
class Foo
{
[StringLength(15)]
public string MyProperty { get; set; }
string myField;
[StringLength(20)]
public string OtherProperty { get { return myField; } set { myField = value; } }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.