简体   繁体   中英

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:

    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 . Is it possible to get the FieldInfo or target member from the property get method?

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. 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 . Remember to use the BitConverter when reading the tokens from the stream to resolve them.

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. Maybe you can define a few patterns that the C# compiler will emit for simple and autoimplemented properties. If you find anything different there is no other way as to resign and throw an exception, because the getter could contain any code. 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.

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.

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

You can not. Property can have no backing fields or sets of backing fields. Even set property can have no backing fields at all.

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

What are you expecting to get in FieldInfo?

Property is just a syntax sugar for a pair of set/get methods, or mutators. 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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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