I introduced C# Linq on the previous article #1.
Today’s theme is “Access private members”.
Attention Link to heading
This article will show you how to use System.Reflection
to access private members. However, please do so at your own risk. (It’s not a dangerous operation, but if you do it for codes other people wrote, it can be a copyright infringement.)
I’ll show give a quiz to you! Link to heading
You have been given a variable of the following class.
Apparently there is a float variable in this class, which is initialized with a secret value.
Mr. Capra wants to know the secret value because he loves to reveal secrets.
How can he get a secret value?
public class HasSecret
{
private float ????? = ?????;
public float dummy1 = 3.14f;
private int dummy2 = 314;
}
It’s easy! We just use disassemble tools! Link to heading
Certainly. You can do it instantly by using dnSpy, a disassembler which is extremely powerful in C#. But do so, this article goes meaningless…
So let’s access the variables in HasSecret
from the C# code without disassemblers.
Access straight Link to heading
First, try accessing dummy2
without thinking.
var secret = new HasSecret();
Console.WriteLine(secert.dummy2);
error CS0122: 'HasSecret.dummy2' is inaccessible due to its protection level
Of course an error occurred. If you can access it by this way, there is no point hiding implementation.
You don’t even know the variable name. Do you want to find the name by enumeration ? It’s non sense.
So what should we do?
Use System.Reflection Link to heading
Use System.Reflection
to solve this problems.System.Reflection
is a namespace with classes that provide a way to handle things dynamically using meta information.
Let’s use them to access private variables !
Use the GetType
function to get the type dynamically. You can also use typeof
keyword, which do statically.
Note that they give back a type as System.Type
.
// using System.Reflection;
var secret = new HasSecret();
var type = secret.GetType();
After you receive the type as a variable, call GetFields
. As you can imagine from the name, it is a function that gets the information of all the fields that satisfy the specific conditions. By the way, the return value’s type is System.Reflection.FieldInfo[]
.
var members = type.GetFields(
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic
); // FieldInfo[]
// Show each field names
foreach(var member in members)
{
Console.WriteLine(member.Name);
}
$ dotnet run
password
dummy1
dummy2
We get the names of all the fields contained in HasSecret
.
There is a variable called password
in that output. It’s so strange, isn’t it? Next, let’s read the value of it.
Access private members Link to heading
Use GetFields
and handle the result with calling Where
and FirstOrDefault
.
As result, we get the FieldInfo
of the Field whose name is password
.
Then call GetValue
. It is a function that gets the value of the variable from the instance based on the information in FieldInfo
.
Note that GetValue
returns the value as System.Object
.
// using System.Linq;
var passwordVal = members // FieldInfo[]
.Where(info => info.Name == "password") // IEnumerable<FieldInfo>
.FirstOrDefault() // FieldInfo
.GetValue(secret); // Object
Console.WriteLine((float)passwordVal); // Cast to float
(Note: Strictly speaking, the return value of GetValue
is object?
but not object
. I will introduce how ?
works later.)
$ dotnet run
2.71828
This is the value of password
. It seems that it is the number of Napier. Now that we have the full picture of HasSecret
, we have achieved our goal!
public class HasSecret
{
private float password = 2.71828f;
public float dummy1 = 3.14f;
private int dummy2 = 314;
}
Change the value of a private variable Link to heading
However, Mr. Capra, who doesn’t like the answer being the number of Napiers, wants to change password
to the pi.
This can also be done by a similar operation as above.
Just get the variable FieldInfo
with the name password
and call the function SetValue
in the same way as when you got the value.
members
.Where(info => info.Name == "password")
.FirstOrDefault()
.SetValue(secret, 3.1415f);
Memo Link to heading
Except for some situations, the heavy use of System.Reflection
is an evil road and its performance is much worse than accessing directly. (Although speeding up using Delegate
or using GetValueDirect
will be somewhat faster.)
If you really want to access private members, or if you want to analyze a library whose source code has been lost, please use it. (~~ In that case, dnSpy is overwhelmingly convenient.~~)