代码之家  ›  专栏  ›  技术社区  ›  idude

不带Setter的实例的Set属性

  •  1
  • idude  · 技术社区  · 5 年前

    假设我有一个类和一个接口,如下所示:

    [Serializable]
    public class MyClass : IClass
    {
       public int Prop => 42;
    }
    
    public interface IClass
    {
       int Prop { get; }
    }
    

    正如你所见,没有二传手。但我希望用反射来改变道具属性,但我似乎做不到。以下是我目前掌握的情况:

    var property = typeof(MyClass).GetProperty("Prop", BindingFlags.Instance | BindingFlags.Public);
    property.SetValue(instaneOfClass, 31);
    

    我一直在犯这样的错误: System.ArgumentException: Property set method not found.

    1 回复  |  直到 5 年前
        1
  •  3
  •   lrpe    5 年前

    自动实现的属性具有名为 <PropName>k__BackingField .

    在您的情况下,您可以这样访问此支持字段:

    var property = typeof(MyClass).GetField("<Prop>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
    property.SetValue(instanceOfClass, 31);
    

    注意我在打电话 GetField() 而不是 GetProperty() ,并使用 BindingFlags.NonPublic 而不是 BindingFlags.Public

    但是,由于这是一个表达式体成员(lambda语法),因此不会生成支持字段。 getter不依赖于另一个成员,只返回给定表达式的结果,编译器不需要生成支持字段。

    使用显式getter实现您的属性将生成一个支持字段,上面的代码将起作用。

    public class MyClass : IClass
    {
       public int Prop { get; } = 42;
    }
    
        2
  •  0
  •   lidqy    5 年前

    如果指定一个getter,那么编译器将生成一个包含属性名的字段。然后可以使用反射(GetFields,SetValue)修改此字段的值:

    public class Foo        {
            public Foo() => Bar = 42;
            public int Bar { get; }        
    
            static void Main()  {
               var f = new Foo();
               Console.WriteLine(f.Bar); // ==> 42;
               f.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).First(x => x.Name.Contains("Bar")).SetValue(f, 43);
               Console.WriteLine(f.Bar); // ==> 43;
      }
    }
    

    它不适用于示例中的计算属性。