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

Ron*_*ein 111 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
Run Code Online (Sandbox Code Playgroud)

Phi*_*ert 144

您可以:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);
Run Code Online (Sandbox Code Playgroud)

  • 在 .NET 5 中不起作用,自 .NET Core 3.0 以来,此行为有记录的更改。“此方法不能用于可靠地设置静态‘init-only’(C# 中的‘readonly’)字段的值。在 .NET Core 3.0 及更高版本中,如果尝试在静态上设置值,则会引发异常,‘仅限初始化’字段。” (3认同)
  • 当然,你是对的.我很抱歉.是的,我确实尝试过,但我试图直接设置readonly属性,而不是使用支持字段.我的尝试毫无意义.你的解决方案完美无缺(再次测试,这次正确) (2认同)
  • 在 dotnet core 3.0 中,这不再可能。抛出 System.FieldAccessException 并表示:“在初始化类型 'Foo' 后,无法设置 initonly 静态字段 'bar'。” (2认同)

Jon*_*eet 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);
    }        
}
Run Code Online (Sandbox Code Playgroud)

这很好用.(Java有不同的规则,有趣的是 - 你必须明确地设置它Field是可访问的,并且它只适用于实例字段.)

  • 我还注意到,仅仅因为你今天可以在某些实现中并不意味着你可以在所有时间进行每次实现.我不知道我们在哪里记录只读字段必须通过反射可变.据我所知,CLI的一致性实现完全可以自由地实现只读字段,以便在构造函数完成后通过反射进行变异时抛出异常. (25认同)
  • @drifter:那时候,你正在打开一个痛苦的世界.您依赖于当前的实现细节,这些细节可以在将来的版本中轻松更改. (5认同)
  • 艾哈迈德 - 但是我们没有使用这种语言来做,所以语言规范没有得到投票...... (4认同)
  • 是的 - 有很多可以做到的"打破"语言需要的东西.例如,您可以多次运行类型初始值设定项. (4认同)
  • 但这并不好,因为有些情况下我需要扩展一个类,而不是最初设计的类.应该总是有一种方法来在计划周密时覆盖封装.唯一的选择是血腥的,有时候不可能不重写部分框架. (3认同)
  • @ kunj2aan:你传入null作为"目标".请参阅"FieldInfo.SetValue"的文档. (2认同)
  • @EricLippert:这是我期望的行为. (2认同)
  • @LouisRhys:根据我的经验,*通常*建议您退后一步,尝试制定不同的设计. (2认同)

And*_*eas 12

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

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

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

  • 有兴趣知道哪些环境会抛出 VerificationException (2认同)

小智 5

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

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

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

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

  • 我还使用它来克服一些 api 限制,其中值要么是硬编码的,要么需要我无法提供的配置文件。(例如,通过反射加载程序集时 DIME 附件的 WSE 2.0 文件大小) (2认同)

小智 5

不要这样做。

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

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

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

所以不要这样做。

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

  • 仅供参考 - Unity 引擎无法用于有效回答诸如此类的深层 C# 语言特定问题,因为从某种意义上说,Unity 执行自己的 C# 编译,就好像 .cs 是脚本一样。我并不是说您的观点无效,但它肯定是针对 Unity 引擎和 C# 的。 (4认同)