[英]Get PropertyInfo of a C# auto property given the backing field
我正在實現一個自定義IFormatter,將對象序列化為我們的遺留系統所需的自定義格式。
如果我聲明一個C#auto屬性:
[StringLength(15)]
public MyProperty { get; set; }
然后在我的自定義序列化方法中,我通過以下方式獲取序列化字段:
MemberInfo[] members =
FormatterServices.GetSerializableMembers(graph.GetType(), Context);
如何訪問裝飾auto屬性的StringLength屬性?
我目前通過利用<PropertyName>k_backingfield
命名約定獲取屬性信息。 我寧願不依賴它,因為它似乎是C#編譯器實現的具體細節。 有沒有更好的辦法?
更好的方法是停止依賴私有字段進行序列化(如FormatterServices.GetSerializableMembers
返回),而只使用公共屬性。
它是一個很多清潔工,在這種特殊情況下工作。
但由於遺留代碼,您可能希望繼續使用FormatterServices.GetSerializableMembers
,在這種情況下,除了使用命名約定(或者一點IL分析)之外,沒有其他選項,並且它可能會在每個新的編譯器發布。
只是為了好玩,這里有一些代碼可以做一些IL分析(它缺少忽略NOOP和其他細節,但應該適用於大多數當前的編譯器。如果你真的采用這樣的解決方案,請檢查Cecil庫(由Jb Evain編寫),因為它包含一個完整的反編譯器,它比手工完成更好。
它的用法是這樣的:
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();
}
}
和代碼:
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.