Hi All,
Since this took me longer to achieve than I'm comfortable admitting.. Here is a quick guide on how to use reflections to access private fields in C#
Note from Ryan: Reflection is a very power hungry method. You should try to avoid using it as it can add some noticeable latency to a server.
Experienced developers will beat you with a stick if they see a reflection in your coding.
Using a Reflection in the way I have outlined in the example is extremely undesirable. If you feel you cannot achieve what you are trying to do without calling a reflection, think of a way you can heavily limit the amount of times the reflection is called.
Please also note, the terminology may not be 100% correct, but this a noobs guide and I have written it in a way to aid in the explanation of the process without the confusion of unknown terminology.
In this case I'll be accessing a private field, namely the lifeStory field located within the BasePlayer class, which is an Object of the PlayerLifeStory class.
Above is the end result to access the private field "lifeStory" within the BasePlayer class.Code:PlayerLifeStory lifeStory = typeof(BasePlayer).GetField("lifeStory", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(player) as PlayerLifeStory;
To break this down into digestible chunks is;
1) Declare an object of the class you are retrieving(in this case, PlayerLifeStory)
2) Obtain the typeof BasePlayer as the GetField method resides within thisCode:PlayerLifeStory lifeStory = ...
3) Utilize the GetField method with the parameters "lifeStory"(being the private field you want to access) and the BindingFlags: BindingFlags.NonPublic & BindingFlags.Instance(You may have to utilize BindingFlags.Static or BindingFlags.Public accordingly also)Code:PlayerLifeStory lifeStory = typeof(BasePlayer)....
4) Utilize the GetValue method which will then associate the Field with an Object(player) of the Type you retrieved in step 2(BasePlayer in this case)Code:PlayerLifeStory lifeStory = typeof(BasePlayer).GetField("lifeStory", BindingFlags.Instance | BindingFlags.NonPublic)...
5) Cast the result of GetValue to the Class of the of the private field you are trying to accessCode:PlayerLifeStory lifeStory = typeof(BasePlayer).GetField("lifeStory", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(player)
6) Rejoice in your success! You can now utilize the Private Field you were trying to access as if it was a public field. For example:Code:PlayerLifeStory lifeStory = typeof(BasePlayer).GetField("lifeStory", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(player) as PlayerLifeStory
Now that you know how to access private fields, reference this post and you will be able to work backwards to access private methods or set values of fields etc.Code:private object OnPlayerLand(BasePlayer player) { PlayerLifeStory lifeStory = typeof(BasePlayer).GetField("lifeStory", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(player) as PlayerLifeStory; if(lifeStory.secondsAlive< 3) { Puts("Player has been alive for more than 3 seconds"); } }
[NoobGuide] Using Reflections to Access Private Fields of an Object C#
Discussion in 'Rust Development' started by pinkstink, Dec 20, 2017.
-
You lost me at Hi All
-
-
Please bare in mind reflection is a very power hungry method. So doing it as frequently as you do is strongly unreccomended from me.
That hook is an extremely frequent hook to be called and using reflection everytime it’s called will most likely add some noticeable latency to the server. -
-
In this specific case, reflection isn't needed (though I realize the point of this thread is more than that). BasePlayer.TimeAlive() should return lifestory.secondsAlive.
-
Wulf Community Admin
If you do have something you think should be public, just let us know and we will often expose it.
-
-
I did a benchmark of getting the time alive through lifestory reflection vs BasePlayer.TimeAlive(), ran a loop 1000 times. The reflection one took 0.1428ms, the non-reflection took 0.0215ms.
For what it's worth, I declared the FieldInfo outside of the loop, and was just calling .GetValue each time. I'm sure there is some difference there, but I think it's bad practice to be doing GetField every time you want to access it anyway. -
-
Snippet I used for testing (lifeStory fieldinfo not included as it's outside):
Code:var rngPlayer = BasePlayer.activePlayerList.GetRandom(); if (rngPlayer != null) { var watch = new Stopwatch(); watch.Start(); for(int i = 0; i < 1000; i++) { var life = lifeStory.GetValue(rngPlayer); var timeAlive = (life as ProtoBuf.PlayerLifeStory)?.secondsAlive; } watch.Stop(); PrintWarning("lifeStory.GetValue x1000 took: " + watch.Elapsed.TotalMilliseconds + "ms"); watch.Reset(); watch.Start(); for (int i = 0; i < 1000; i++) { var timeAlive = rngPlayer.TimeAlive(); } watch.Stop(); PrintWarning("BasePlayer.TimeAlive() x1000 took: " + watch.Elapsed.TotalMilliseconds + "ms"); watch.Reset(); watch.Start(); for (int i = 0; i < 1000; i++) { var lifeField = typeof(BasePlayer).GetField("lifeStory", (BindingFlags.Instance | BindingFlags.NonPublic)); var life = lifeField.GetValue(rngPlayer); var timeAlive = (life as ProtoBuf.PlayerLifeStory)?.secondsAlive; } watch.Stop(); PrintWarning("lifeField x1000 took: " + watch.Elapsed.TotalMilliseconds + "ms"); }
-
-
Reflection is a means to an end in the case of plugin development as you are working with a codebase that isn't yours and you sometimes want or need to use the values of private fields/properties or invoke private methods. It all depends on how, where and when you use it.
For those interested I can suggest having a look at this: Why is reflection slow? -
If you use private members -> create feature request post Feature Suggestions | Oxide
Example: I need public access to private field BasePlayer.???
And Wulf will add this