【问题标题】:Changing read only properties with reflection使用反射更改只读属性
【发布时间】:2010-09-14 05:50:51
【问题描述】:

有可能吗?用反射还是其他方式?

【问题讨论】:

  • Élodie:有可能,但是不要这样做!!!
  • 您是要更改仅使用 getter 的属性的返回值,还是更改只读支持字段的值(如 private readonly int primitiveValue = 1;)?
  • @Gabe:总的来说,我同意。我不得不实际使用它来调试一些我没有源代码的代码。当您需要强制代码执行某些操作以在调试时暴露错误行为时,它很有用。
  • 在反序列化其中一个只读属性是集合的对象时,我需要这样做。这是您可能想要这样做的另一个“好”原因。仅供参考,在非只读支持字段上调用反射更容易和更清晰。

标签: c# reflection


【解决方案1】:

如前所述,如果您需要这样做,那么您一开始就面临设计问题。现在,如果您想知道这是否可能只是为了了解,或者如果地球上没有其他方法可以做到这一点,那么借助非常小的helper library 和扩展方法,它确实是可能的。

考虑以下代码:

class Person {

    int age;
    string name;

    public int Age { get { return age; } }
    public string Name { get { return name; } }
}

// ...

using Mono.Reflection;
using System.Reflection;

// ...

Person person = new Person (27, "jb");
PropertyInfo nameProperty = typeof (Person).GetProperty ("Name");
FieldInfo nameField = nameProperty.GetBackingField ();
nameField.SetValue (person, "jbe");

使用此代码,您可以仅使用属性获取属性的支持字段,并为支持字段分配一个新值。您可以阅读有关implementation 的更多详细信息。

另请注意,它仅适用于简单属性,例如:

public int Age { get { return age; } }

public string Name {
    get { return name; }
    set { name = value; }
}

public double Velocity { get; private set; }

如果您有包含自定义代码的复杂属性(包括 expression-bodied member,如 int Answer=> 42;),则支持字段解析器将失败,因为在这种情况下没有支持字段。

【讨论】:

    【解决方案2】:

    Simon Mattes 的替代答案是

    假设你有:

    public class MyClass
    {
         public int MyNumber {get;}
    }
    

    如果出于测试目的,您可以这样做:

    var field = typeof(MyClass).GetField("<MyNumber>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
    field.SetValue(anIstanceOfMyClass, 3);
    

    【讨论】:

    • 它的神奇文件名"&lt;MyNumber&gt;k__BackingField"work)
    【解决方案3】:

    作为自动生成的只读属性的一个非常肮脏的解决方法:

    class MyTestClass
    {
        public string MyProperty { get; }
    
        public MyTestClass( string MyProperty )
        {
            this.MyProperty = MyProperty;
        }
    }
    

    您可以通过以下方式修改自动生成的支持字段:

    MyTestClass MyClass = new MyTestClass( "Hello" );
    FieldInfo MyWriteableField = MyClass.GetType().GetRuntimeFields().Where( a => Regex.IsMatch( a.Name, $"\\A<{nameof( MyClass.MyProperty )}>k__BackingField\\Z" ) ).FirstOrDefault();
    MyWriteableField.SetValue( MyClass, "Another new value" );
    

    PS:当您使用

    【讨论】:

      【解决方案4】:

      这取决于属性。如果它是一个计算属性 - 不,除非你知道它的基础是什么。如果它只是一个私有字段的访问器,那么您可以尝试修改该字段。

      然而,总的来说,这是一个非常糟糕的主意,因为您可能不知道这会导致什么副作用。

      【讨论】:

        【解决方案5】:
        PropertyInfo isReadOnly = typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance| BindingFlags.NonPublic);
        //Remove the readonly property
        isReadOnly.SetValue(param, false, null);
        
        //.............put your code here.....
        
        // Set readonly property
        isReadOnly.SetValue(param, true, null);
        

        【讨论】:

        • 我已经尝试过了,但它不起作用。您所做的只是将属性设置为 false,然后再设置为 true。
        猜你喜欢
        • 2013-08-06
        • 1970-01-01
        • 2021-03-27
        • 2016-12-17
        • 2015-01-21
        • 2018-12-15
        • 2018-02-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多