简体   繁体   English

如何从PropertyInfo获取Getter支持字段?

[英]How to get Getter backing field from PropertyInfo?

I have a class as follows: 我有一个课程如下:

class Foo : PropertyChangedBase {
    private int _property;

    public int Property {
       get { return _property; }
       set { OnAssignPropertyChanged("Property", () => _property, value); }
}

PropertyChangedBase implements INotifyPropertyChanged with the following methods: PropertyChangedBase使用以下方法实现INotifyPropertyChanged

    protected void OnAssignmentPropertyChanged<T>(string propertyName, Expression<Func<T>> fieldExpression, T value)
    {
        var get = fieldExpression.Compile();
        if (get().Equals(value))
        {
            return;
        }

        //  invoke set property method
        SetProperty(fieldExpression, value);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private void SetProperty<T>(Expression<Func<T>> fieldExpression, T value)
    {
        if (fieldExpression == null)
        {
            throw new ArgumentNullException(nameof(fieldExpression));
        }

        var memberExpression = fieldExpression.Body as MemberExpression;
        if (memberExpression == null)
        {
            throw new ArgumentException("fieldExpression");
        }

        var field = memberExpression.Member as FieldInfo;
        if (field == null)
        {
            throw new ArgumentException("fieldExpression");
        }

        field.SetValue(this, value);
    }

I would prefer to call: 我更愿意打电话:

OnAssignPropertyChanged(() => Property, value);

The only way this will work is if I can get the backing field for the property getter and then pass that to SetProperty . 唯一可行的方法是,如果我可以获取属性getter的支持字段,然后将其传递给SetProperty Is it possible to get the FieldInfo or target member from the property get method? 是否可以从属性get方法获取FieldInfo或目标成员?

As a general answer, yes you can do, at least under controlled conditions. 作为一般答案,是的,你可以做到,至少在受控条件下。 But the only case you should do this is when you are absoluty sure what you are doing and only with limited support, because there will be cases you can not handle. 但是你唯一应该这样做的情况就是当你绝对确定自己在做什么而且只有有限的支持时,因为有些情况你无法处理。

Have a look at the answer here: Find all property references using reflection . 看看这里的答案: 使用反射查找所有属性引用 The target is a bit different but the approach is similar for finding field references. 目标有点不同,但方法类似于查找字段引用。 Since the answer there already includes the necessary code i will just outline the way to go: 由于答案已经包含了必要的代码,我将概述要走的路:

All metadata items in .Net are referenced by tokens. .Net中的所有元数据项都由标记引用。 To get tokens used inside a method you have to parse the MethodBody (by skipping all the things you wont inspect) and then resolve the found tokens in their module . 要获取在方法中使用的标记,您必须解析MethodBody (通过跳过您不会检查的所有内容),然后在其模块中解析找到的标记 Remember to use the BitConverter when reading the tokens from the stream to resolve them. 记住在从流中读取令牌时使用BitConverter来解决它们。

But now to the down side; 但现在向下; the only time you can really safely use this to find the backing fields of a properties getter, is when you find a simple get method, with a well defined opcode sequence like Ldfld, Ret or something like that. 唯一一次你可以真正安全地使用它来查找属性getter的支持字段,就是当你找到一个简单的get方法时,有一个定义良好的操作码序列,如Ldfld,Ret或类似的东西。 Maybe you can define a few patterns that the C# compiler will emit for simple and autoimplemented properties. 也许您可以定义一些C#编译器将为简单和自动实现的属性发出的模式。 If you find anything different there is no other way as to resign and throw an exception, because the getter could contain any code. 如果你发现任何不同的东西,没有别的方法可以辞职并抛出异常,因为getter可以包含任何代码。 Like always with reflection, only use whitelist approaches, check for the conditions you expect and throw exeptions in any other case or you will run into a NullReferenceException sooner or later. 就像使用反射一样,只使用白名单方法,检查您期望的条件并在任何其他情况下抛出exeptions,否则您迟早会遇到NullReferenceException。

If this is worth the trouble is for you to decide, but in general you could do this since .Net 2.0 and do not even need a fancy external lib. 如果这是值得的,你可以自己决定,但一般来说你可以这样做.Net 2.0,甚至不需要花哨的外部库。

No, in general case you can't . 不,一般情况下你不能 Just compare two classes: 只比较两个类:

public class Test {
  private int _propA;
  private int _propB;

  public int PropA {get { return _propA; }}
  public int PropB {get { return _propB; }}
} 

public class TestSwapped {
  private int _propA;
  private int _propB;

  // please, notice swapped backing fields
  public int PropA {get { return _propB; }}
  public int PropB {get { return _propA; }}
} 

you'll get identical PropertyInfo[] and FieldInfo[] arrays but different backing fields 你会获得相同的PropertyInfo[]FieldInfo[]数组但不同的支持字段

You can not. 你不能。 Property can have no backing fields or sets of backing fields. 属性可以没有支持字段或支持字段集。 Even set property can have no backing fields at all. 即使是set属性也根本没有后备字段。

public Int32 Prop
{
set { Debug.WriteLine(value.ToString()); }
get { return 1; }
}

What are you expecting to get in FieldInfo? 您期望在FieldInfo中获得什么?

Property is just a syntax sugar for a pair of set/get methods, or mutators. 属性只是一对set / get方法或mutator的语法糖。 Being a method allows them to contain as much code as needed, including being just empty and, of course, there is no requirement to have a backing field from compiler perspective. 作为一种方法,它们可以根据需要包含尽可能多的代码,包括只是空的,当然,不需要从编译器角度来看具有支持字段。

In pursuing this for a different question, here is an extension method for the simple cases - an autogenerated backing field, or a get that just returns a backing field: 在针对不同的问题进行此操作时,以下是针对简单情况的扩展方法 - 自动生成的支持字段,或仅返回支持字段的get:

public static class MethodInfoExt {
    const int IL_ldarg0 = 0x02;
    const int IL_ldfld = 0x7B;

    public static FieldInfo FieldInfoFromGetAccessor(this MethodInfo getAccessorMI) {
        var body = getAccessorMI.GetMethodBody().GetILAsByteArray();
        if (body[0] == IL_ldarg0 && body[1] == IL_ldfld) {
            var fieldToken = BitConverter.ToInt32(body, 2);
            return getAccessorMI.DeclaringType.Module.ResolveField(fieldToken);
        }
        else
            return default;
    }    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM