我可以使用反射更改 C# 中的私有只读字段吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/934930/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I change a private readonly field in C# using reflection?
提问by Ron Klein
I am wondering, since a lot of things can be done using reflection, can I change a private readonly field after the constructor completed its execution?
(note: just curiosity)
我想知道,既然很多事情都可以使用反射来完成,那么我可以在构造函数完成执行后更改私有只读字段吗?
(注:只是好奇)
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
采纳答案by Philippe Leybaert
You can:
你可以:
typeof(Foo)
.GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
.SetValue(foo,567);
回答by Jon Skeet
The obvious thing is to try it:
显而易见的是尝试一下:
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);
}
}
This works fine. (Java has different rules, interestingly - you have to explicitly set the Field
to be accessible, and it will only work for instance fields anyway.)
这工作正常。(有趣的是,Java 有不同的规则——你必须明确地将 设置Field
为可访问的,而且它只适用于实例字段。)
回答by Powerlord
The answer is yes, but more importantly:
答案是肯定的,但更重要的是:
Why would you want to? Intentionally breaking encapsulation seems like a horrifically bad idea to me.
你为什么要?故意破坏封装对我来说似乎是一个可怕的坏主意。
Using reflection to change a readonly or constant field is like combining the Law of Unintended Consequenceswith Murphy's Law.
回答by Necroposter
You asked why you would want to break the encapsulation like that.
您问为什么要像这样破坏封装。
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().
我使用实体助手类来水合实体。这使用反射来获取新的空实体的所有属性,并将属性/字段名称与结果集中的列匹配,并使用 propertyinfo.setvalue() 设置它。
I don't want anyone else to be able to change the value, but I don't want to take all the effort to custom code hydration methods for every entity either.
我不希望其他任何人能够更改该值,但我也不想尽一切努力为每个实体自定义代码水化方法。
My many of my stored procs return resultsets that don't correspond directly to tables or views, so the code gen ORM's do nothing for me.
我的许多存储过程返回的结果集不直接对应于表或视图,因此代码生成 ORM 对我没有任何作用。
回答by Andreas
I agree with the other answers in that it works generallyand especially with the comment by E. Lippert that this is not documented behavior and therefore not future-proof code.
我同意其他答案,因为它通常有效,尤其是 E. Lippert 的评论,即这不是记录在案的行为,因此不是面向未来的代码。
However, we also noticed another issue. If you're running your code in an environment with restricted permissions you might get an exception.
然而,我们也注意到了另一个问题。如果您在权限受限的环境中运行代码,则可能会出现异常。
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.
我们刚刚遇到过一个案例,我们的代码在我们的机器上运行良好,但是VerificationException
当代码在受限环境中运行时我们收到了。罪魁祸首是对 readonly 字段的 setter 的反射调用。当我们删除该字段的只读限制时,它起作用了。
回答by Dudeman3000
I just want to add that if you need to do this stuff for unit testing, then you can use:
我只想补充一点,如果您需要为单元测试做这些事情,那么您可以使用:
A) The PrivateObjectclass
A) PrivateObject类
B) You will still need a PrivateObject instance, but you can generate "Accessor" objects with Visual Studio. How to: Regenerate Private Accessors
B) 您仍然需要一个 PrivateObject 实例,但您可以使用 Visual Studio 生成“访问器”对象。 如何:重新生成私有访问器
If you are setting private fields of an object in your code outside of unit testing, that would be an instance of "code smell" I think that perhaps the only other reason you would want to do this is if you are dealing with a third party library and you can't change the target class code. Even then, you probably want to contact the 3rd party, explain your situation and see if they won't go ahead and change their code to accomodate your need.
如果您在单元测试之外的代码中设置对象的私有字段,那将是“代码异味”的一个实例,我认为您可能想要这样做的唯一其他原因是您正在与第三方打交道库,您不能更改目标类代码。即便如此,您可能还是想联系第 3 方,说明您的情况,看看他们是否会继续更改代码以满足您的需要。
回答by Tynan Sylvester
Don't do this.
不要这样做。
I just spent a day fixing a surreal bug where objects could be not of their own declared type.
我刚刚花了一天时间修复了一个超现实的错误,其中对象可能不是它们自己声明的类型。
Modifying the readonly field worked once. But if you tried to modify it again, you'd get situations like this:
修改只读字段工作一次。但是如果你再次尝试修改它,你会遇到这样的情况:
SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field;
if( !(mySound is SoundDef) )
Log("Welcome to impossible-land!"); //This would run
So don't do it.
所以不要这样做。
This was on the Mono runtime (Unity game engine).
这是在 Mono 运行时(Unity 游戏引擎)上。
回答by zezba9000
Another simple way to do this using unsafe (or you could pass the field to a C method via DLLImport and set it there).
使用 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);
}
}
}