121

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

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
4

8 回答 8

160

你可以:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);
于 2009-06-01T13:59:10.610 回答
54

显而易见的是尝试它:

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

于 2009-06-01T13:59:56.630 回答
11

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

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

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

于 2013-02-26T11:36:19.330 回答
4

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

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

我不希望其他人能够更改该值,但我也不希望花费所有精力为每个实体自定义代码水合方法。

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

于 2010-01-27T21:20:41.327 回答
4

不要这样做。

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

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

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

所以不要这样做。

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

于 2014-05-12T17:52:33.707 回答
3

另一种使用 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);
        }
    }
}
于 2018-02-21T23:19:41.147 回答
2

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

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

使用反射来更改只读或常量字段就像将意外后果定律与墨菲定律结合起来。

于 2009-06-01T14:21:04.950 回答
0

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

A) PrivateObject

B) 您仍然需要 PrivateObject 实例,但您可以使用 Visual Studio 生成“访问器”对象。 如何:重新生成私有访问器

如果您在单元测试之外的代码中设置对象的私有字段,那将是“代码异味”的一个实例,我认为您想要这样做的唯一其他原因可能是您正在与第三方打交道库,您无法更改目标类代码。即便如此,您可能还是想联系第 3 方,说明您的情况,看看他们是否会继续更改代码以适应您的需要。

于 2013-05-24T14:40:00.003 回答