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

时间:2009-06-01 13:56:42

标签: c# reflection field readonly

我很想知道,因为使用反射可以完成很多事情,我可以在构造函数完成执行后更改私有只读字段吗?
(注意:只是好奇心)

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 个答案:

答案 0 :(得分:142)

你可以:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);

答案 1 :(得分:52)

显而易见的是尝试它:

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是可访问的,并且它只适用于实例字段。)

答案 2 :(得分:12)

我同意其他答案,因为它通常 ,尤其是E.Lippert的评论,这不是记录在案的行为,因此也不是未来的代码。

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

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

答案 3 :(得分:4)

你问为什么要打破这样的封装。

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

我不希望任何其他人能够更改该值,但我不想为每个实体采取所有的自定义代码水合方法。

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

答案 4 :(得分:2)

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

你为什么要这样?故意破坏封装对我来说似乎是一个可怕的坏主意。

使用反射来更改只读或常量字段就像将Law of Unintended ConsequencesMurphy's Law结合起来。

答案 5 :(得分:2)

不要这样做。

我花了一天时间来修复一个超现实的bug,其中的对象可能不属于他们自己声明的类型。

修改只读字段一次。但如果你试图再次修改它,你会遇到这样的情况:

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

所以不要这样做。

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

答案 6 :(得分:2)

使用unsafe执行此操作的另一种简单方法(或者您可以通过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);
        }
    }
}

答案 7 :(得分:0)

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

A)PrivateObject

B)您仍然需要一个PrivateObject实例,但您可以使用Visual Studio生成“Accessor”对象。 How to: Regenerate Private Accessors

如果你在单元测试之外的代码中设置一个对象的私有字段,那将是一个“代码味道”的实例我认为也许你想要做的唯一其他原因是如果你正在处理第三方库,您无法更改目标类代码。即便如此,您可能希望联系第三方,解释您的情况,看看他们是否会继续并改变他们的代码以满足您的需求。