Can I change a private readonly field in C# using reflection?

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

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
57749 次浏览

你可以:

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

显而易见的事情是尝试一下:

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设置为可访问,而且它只能在实例字段中工作。)

The answer is yes, but more importantly:

你为什么要这么做? 故意破坏封装对我来说是个糟糕透顶的主意。

使用反射更改只读字段或常量字段就像将 意外后果法则墨菲定律组合一样。

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

I use an entity helper class to hydrate entities. This uses reflection to get all the properties of a new empty entity, and matches the property/field name to the column in the resultset, and set's it using propertyinfo.setvalue().

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

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

我同意其他的回答,因为它可以工作在 一般来说中,特别是 E。 Lippert 的评论,即这不是文档化的行为,因此不是未来的代码。

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

We've just had a case where our code worked fine on our machines, but we received a VerificationException when the code ran in a restricted environment. The culprit was a reflection call to the setter of a readonly field. It worked when we removed the readonly restriction of that field.

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

A) PrivateObject课程

B)您仍然需要一个 PrivateObject 实例,但是您可以使用 VisualStudio.如何: 重新生成专用访问器生成“ Accessor”对象

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

别这样。

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

修改 readonly 字段只有一次效果,但如果再次修改它,就会出现以下情况:

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

So don't do it.

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

另一个简单的方法是使用不安全的方法(或者您可以通过 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);
}
}
}