为什么以下代码不起作用:
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:
试试这个(灵感来自于通过反射找到一个私有字段?):
var prop = s.GetType().GetField("id", System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance);
prop.SetValue(s, "new value");
我的更改是使用
GetField
方法 - 您访问的是字段而不是属性,并且使用 NonPublic
或 Instance
。
显然,添加
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]
>
如果您想要更快的替代方案,请考虑使用表达式树或
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; }
}
扩展尼克的答案:
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.