设置私有字段的值

问题描述 投票:0回答:4

为什么以下代码不起作用:

class Program
{
    static void Main ( string[ ] args )
    {
        SomeClass s = new SomeClass( );

        s.GetType( ).GetField( "id" , System.Reflection.BindingFlags.NonPublic ) // sorry reasently updated to GetField from GetProperty...
            .SetValue( s , "new value" );
    }
}


class SomeClass
{
    object id;

    public object Id 
    {
        get
        {
            return id;
        }
    }   
}

我正在尝试设置私有字段的值。


这是我得到的例外:

 System.NullReferenceException was unhandled   Message=Object reference not set to an instance of an object.   Source=ConsoleApplication7
 StackTrace:
        at Program.Main(String[] args) in C:\Users\Antonio\Desktop\ConsoleApplication7\ConsoleApplication7\Program.cs:line 18
        at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
        at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
        at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
        at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
        at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
        at System.Threading.ThreadHelper.ThreadStart()   InnerException:
c# reflection
4个回答
106
投票

试试这个(灵感来自于通过反射找到一个私有字段?):

var prop = s.GetType().GetField("id", System.Reflection.BindingFlags.NonPublic
    | System.Reflection.BindingFlags.Instance);
prop.SetValue(s, "new value");

我的更改是使用

GetField
方法 - 您访问的是字段而不是属性,并且使用
NonPublic
Instance


1
投票

显然,添加

BindingFlags.Instance
似乎已经解决了这个问题:

> class SomeClass
  {
      object id;

      public object Id
      {
          get
          {
              return id;
          }
      }
  }
> var t = typeof(SomeClass)
      ;
> t
[Submission#1+SomeClass]
> t.GetField("id")
null
> t.GetField("id", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
> t.GetField("id", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
[System.Object id]
> 

1
投票

如果您想要更快的替代方案,请考虑使用表达式树或

Unsafe.As<T>
在测试中反射可能比任何一种技术慢 30 倍到 100 倍。

表达式树

static Action<TSource, TArg> BuildSetterExpression<TSource, TArg>(FieldInfo field)
{
    var inputObject = Expression.Parameter(typeof(TSource));
    var valueToSet = Expression.Parameter(typeof(TArg));

    var fieldInformation = Expression.Field(inputObject, field);
    var fieldAssignment = Expression.Assign(fieldInformation, valueToSet);

    return Expression.Lambda<Action<TSource, TArg>>(fieldAssignment, inputObject, valueToSet).Compile();
}

var fieldInfo = typeof(MyClass).GetField("_num", BindingFlags.NonPublic | BindingFlags.Instance);
var setNumFunc = BuildSetterExpression<MyClass, int>(fieldInfo);

var instance = new MyClass();
setNumFunc(instance, 123);
Assert.Equal(123, instance.Num);

class MyClass
{
    private int _num = 12;
    public int Num => _num;
}

不安全。如

如果您知道要访问的对象的结构并且不介意使用不安全代码,请使用

Unsafe.As<T>

var source = new SourceClass();
var target = Unsafe.As<TargetClass>(source);

target.Num = 123;
Assert.Equal(123, source.GetPrivateNum);

class SourceClass
{
    private int _num = 12;
    public int GetPrivateNum => _num;
}

class TargetClass
{
    public int Num { get; set; }
}

0
投票

扩展尼克的答案:

    public static void SetPrivateField<T, T2>(this T item, string fieldName, T2 newValue)
    {
        var field = item.GetType().GetField(fieldName, BindingFlags.NonPublic, | BindingFlags.Instance);
        field.SetValue(item, newValue);
    }

要使用这个:

someClassInstance.SetPrivateField("id", "newValue");
//realizes the type T2 by the fact "newValue" is a string.
© www.soinside.com 2019 - 2024. All rights reserved.