我可以使用反射更改C#中的私有隻讀字段嗎?




reflection field (7)

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

你為什麼想要? 有意破壞封裝對我來說似乎是一個糟糕的主意。

使用反射來改變只讀或常量場就像將意想不到的後果 定律墨菲定律相結合。

我想知道,因為很多事情都可以使用反射來完成,我可以在構造函數完成執行後更改私有隻讀字段嗎?
(注意:只是好奇)

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

我同意其他答案,因為它的工作原理一般 ,特別是E. Lippert評論說這不是記錄行為,因此也不是面向未來的代碼。

但是,我們也注意到另一個問題。 如果您在具有有限權限的環境中運行代碼,則可能會遇到異常。

我們剛剛遇到了一些情況,我們的代碼在我們的機器上運行良好,但是當代碼在受限制的環境中運行時,我們收到了一個VerificationException 。 罪魁禍首是對只讀領域的製定者的反映。 當我們刪除該字段的只讀限制時,它起作用。


另一個簡單的方法是使用不安全的方法(或者你可以通過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);
        }
    }
}

我只想補充一點,如果你需要為單元測試做這件事,那麼你可以使用:

A) PrivateObject

B)您仍然需要一個PrivateObject實例,但您可以使用Visual Studio生成“Accessor”對象。 如何:重新生成專用訪問器

如果你在單元測試之外的代碼中設置了一個對象的私有字段,那將是一個“代碼異味”的實例我想這也許是你想要做的唯一的其他原因是如果你正在與第三方打交道庫,你不能改變目標類的代碼。 即使這樣,你可能也想聯繫第三方,解釋你的情況,看看他們是否不會繼續並改變他們的代碼以適應你的需要。


不要這樣做。

我花了一天的時間修復了一個超現實的bug,其中的對象可能不屬於他們自己的聲明類型。

修改readonly字段一次。 但是如果你試圖再次修改它,你會遇到這樣的情況:

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

所以不要這樣做。

這是在Mono運行時(Unity遊戲引擎)上。


顯而易見的是嘗試它:

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是可訪問的,並且它只能用於實例字段。)


您可以:

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




readonly