代码之家  ›  专栏  ›  技术社区  ›  Ron Klein Noa Kuperberg

我可以使用反射更改C中的私有只读字段吗?

  •  109
  • Ron Klein Noa Kuperberg  · 技术社区  · 16 年前

    我想知道,由于很多事情都可以通过反射来完成,在构造函数完成执行之后,我可以更改一个私有的只读字段吗?
    (注:只是好奇)

    public class Foo
    {
     private readonly int bar;
    
     public Foo(int num)
     {
      bar = num;
     }
    
     public int GetBar()
     {
      return bar;
     }
    }
    
    Foo foo = new Foo(123);
    Console.WriteLine(foo.GetBar()); // display 123
    // reflection code here...
    Console.WriteLine(foo.GetBar()); // display 456
    
    8 回复  |  直到 7 年前
        1
  •  141
  •   Philippe Leybaert    16 年前

    你可以:

    typeof(Foo)
       .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
       .SetValue(foo,567);
    
        2
  •  52
  •   Jon Skeet    16 年前

    显而易见的是尝试一下:

    using System;
    using System.Reflection;
    
    public class Test
    {
        private readonly string foo = "Foo";
    
        public static void Main()
        {
            Test test = new Test();
            FieldInfo field = typeof(Test).GetField
                ("foo", BindingFlags.Instance | BindingFlags.NonPublic);
            field.SetValue(test, "Hello");
            Console.WriteLine(test.foo);
        }        
    }
    

    这个很好用。(Java有不同的规则,有趣的是,您必须显式设置 Field 要访问,它将只在实例字段中工作。)

        3
  •  12
  •   Andreas    12 年前

    我同意其他的答案 通常地 尤其是E.Lippert的评论,这不是文档化的行为,因此也不是未来的证明代码。

    然而,我们也注意到了另一个问题。如果您在具有受限权限的环境中运行代码,则可能会出现异常。

    我们刚收到一个案例,我们的代码在我们的机器上运行良好,但是我们收到了一个 VerificationException 当代码在受限环境中运行时。罪魁祸首是对只读字段的setter的反射调用。当我们删除了该字段的只读限制时,它就工作了。

        4
  •  4
  •   Necroposter    15 年前

    你问为什么要像那样破坏封装。

    我使用实体助手类来水合物实体。这将使用反射获取新空实体的所有属性,并将属性/字段名与结果集中的列匹配,并使用propertyinfo.setvalue()设置它。

    我不希望其他任何人能够改变这个值,但我也不想全力以赴为每个实体定制代码水合方法。

    我的许多存储过程返回的结果集与表或视图不直接对应,因此代码生成ORM对我没有任何作用。

        5
  •  2
  •   Powerlord    16 年前

    答案是肯定的,但更重要的是:

    你为什么要这么做?故意破坏封装对我来说似乎是一个非常糟糕的主意。

    使用反射来更改只读或常量字段就像将 Law of Unintended Consequences 具有 Murphy's Law .

        6
  •  2
  •   Tynan Sylvester    11 年前

    不要这样做。

    我刚刚花了一天时间修复了一个超现实的bug,对象不能是它们自己声明的类型。

    修改只读字段工作一次。但是如果你再次尝试修改它,你会得到这样的情况:

    SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field;
    if( !(mySound is SoundDef) )
        Log("Welcome to impossible-land!"); //This would run
    

    所以不要这样做。

    这是在Mono运行时(Unity游戏引擎)。

        7
  •  2
  •   zezba9000    7 年前

    另一种简单的方法是使用不安全的(或者您可以通过dllimport将字段传递给C方法并将其设置在那里)。

    using System;
    
    namespace TestReadOnly
    {
        class Program
        {
            private readonly int i;
    
            public Program()
            {
                i = 66;
            }
    
            private unsafe void ForceSet()
            {
                fixed (int* ptr = &i) *ptr = 123;
            }
    
            static void Main(string[] args)
            {
                var program = new Program();
                Console.WriteLine("Contructed Value: " + program.i);
                program.ForceSet();
                Console.WriteLine("Forced Value: " + program.i);
            }
        }
    }
    
        8
  •  0
  •   Dudeman3000    12 年前

    我只是想补充一下,如果您需要为单元测试做这些事情,那么您可以使用:

    a) PrivateObject

    b)您仍然需要privateObject实例,但可以使用Visual Studio生成“accessor”对象。 How to: Regenerate Private Accessors

    如果您在单元测试之外设置代码中某个对象的私有字段,那么这就是“代码气味”的一个实例,我认为您可能希望这样做的唯一其他原因是,如果您正在处理第三方库,并且不能更改目标类代码。即便如此,您可能还是希望联系第三方,解释您的情况,看看他们是否会继续进行,并更改他们的代码以满足您的需要。