Jump to content
BloodyPenguin

Tutorial - How to invoke private methods without reflection

8 posts in this topic Last Reply

Highlighted Posts

Posted:
Last Online:  
 

In mods we need to call private methods sometimes. But calling private methods with reflection makes some significant performance hit.

During today's lunch I came up with this simple stupid technique for invoking private methods that uses Sebastian Schöner's  RedirectionHelper. Note that the technique is slightly different for classes and for structs.

using System.Reflection;
using ICities;

namespace TestMod
{
    public class TestMod : IUserMod
    {
        public string Name
        {
          get 
          {
            Test();
            return "Test mod";
          }
        }
        public string Description => "Some test mod";

        public void Test()
        {
            RedirectionHelper.RedirectCalls
            (
                typeof(SomeClassDetour).GetMethod("PublicStaticMethod", BindingFlags.Public | BindingFlags.Static),
                typeof(SomeClass).GetMethod("PrivateInstanceMethod", BindingFlags.NonPublic | BindingFlags.Instance)
            );
            var classInstance = new SomeClass("private value1");
            var arg1 = "method argument1";
            //classInstance.PrivateInstanceMethod(arg1); //That's impossible!
            SomeClassDetour.PublicStaticMethod(classInstance, arg1); //Here we go!

            RedirectionHelper.RedirectCalls
            (
                typeof(SomeStructDetour).GetMethod("PublicStaticMethod", BindingFlags.Public | BindingFlags.Static),
                typeof(SomeStruct).GetMethod("PrivateInstanceMethod", BindingFlags.NonPublic | BindingFlags.Instance)

            );
            var structInstance = new SomeStruct("private value2");
            var arg2 = "method argument2";
            //structInstance.PrivateInstanceMethod(arg2); //That's impossible!
            SomeStructDetour.PublicStaticMethod(ref structInstance, arg2); //Here we go!
        }

        class SomeClass
        {
            private readonly string _privateField;

            public SomeClass(string value)
            {
                _privateField = value;
            }

            private void PrivateInstanceMethod(string arg)
            {
                UnityEngine.Debug.Log($"Class: This is a private method. Private value: {_privateField}. Arg: {arg}");
            }
        }

        class SomeClassDetour
        {
            [MethodImpl(MethodImplOptions.NoInlining)] //to prevent inlining
            public static void PublicStaticMethod(SomeClass instance, string arg)
            {
                //This line is crucial for success! We can't detour empty or too simple methods
                UnityEngine.Debug.Log($"Class: This is a static method. Instance type: {instance?.GetType().ToString() ?? "Null"}. Arg: {arg}"); 
            }
        }

        struct SomeStruct
        {
            private readonly string _privateField;

            public SomeStruct(string value)
            {
                _privateField = value;
            }

            private void PrivateInstanceMethod(string arg)
            {
                UnityEngine.Debug.Log($"Struct: This is a private method. Private value: {_privateField}. Arg: {arg}");
            }
        }

        struct SomeStructDetour
        {
            [MethodImpl(MethodImplOptions.NoInlining)] //to prevent inlining
            public static void PublicStaticMethod(ref SomeStruct instance, string arg)
            {
                //This line is crucial for success! We can't detour empty or too simple methods
                UnityEngine.Debug.Log($"Struct: This is a static method. Instance type: {instance.GetType()}. Arg: {arg}"); 
            }
        }
    }
}

That gives the following output in output_log on game loading:

(Filename: C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 65)

Class: This is a private method. Private value: private value1. Arg: method argument1
 
(Filename: C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 65)

Struct: This is a private method. Private value: private value2. Arg: method argument2

As you see, the private instance method was called successfully. I think it won't be hard for you to figure out by yourself how to apply this technique to properties and to static methods

  • Like 2

Share this post


Link to post
Share on other sites
Posted:
Last Online:  
 

This is a clever idea. Thank you for sharing. As far as I know, Unity/Mono reflection is very slow, so this will give a tremendous speedup after the setup stage (GetMethod() is slow, too).

 

Share this post


Link to post
Share on other sites
Posted:
Last Online:  
 

I'm impressed and have no clue what you're talking about - that's why we need geniuses like the Bloody Bird to make this game really enjoyable...

Thanks for all this!

Dominic

Share this post


Link to post
Share on other sites
Posted:
Last Online:  
 

This is very clever. I took a while until I understood how it works:

It uses the detour library in the opposite way. Usually you are redirecting methods of a game class to methods of a mod class (to replace the behavior of the game).

In this case, your own static "dummy" method of the mod class (the code inside is actually never executed) is redirected to the private method of a game class. Now you can call your public static method to call the private method of the game.

It also uses the trick that the this reference of an instance method is actually a hidden parameter. With that you can pass the this target to your static method, which redirects to the private instance method.

  • Like 1

Share this post


Link to post
Share on other sites
Posted:
Last Online:  
 

A nice and clean way to call private methods. Thank you for sharing!

Share this post


Link to post
Share on other sites
Posted:
Last Online:  
 
On 13.1.2016 at 0:12 PM, Vals Loeder said:

I don't have a single programming cell in my body so I applaud those who do!

We need more mod developers!!! There are so many possibilities that you can't even imagine.

Share this post


Link to post
Share on other sites

Sign In or register to comment...

To comment in reply, you must be a community member

Sign In  

Already have an account? Sign in here.

Sign In Now

Create an Account  

Sign up to join our friendly community. It's easy!  

Register a New Account


×

Thank You for the Continued Support!

Simtropolis depends on donations to fund site maintenance costs.
Without your support, we just would not be in our 24th year online!  You really help make this a great community. *:thumb:

But we still need your support to stay online. If you're able to, please consider a donation to help us stay up and running. This helps sustain a platform where we can share our community creations for years to come.

Make a Donation, Get a Gift!

Expand your city with the best from the Simtropolis Exchange.
Make a Donation and get one or all three discs today!

STEX Collections

By way of a "Thank You" gift, we'd like to send you our STEX Collector's DVD. It's some of the best buildings, lots, maps and mods collected for you over the years. Check out the STEX Collections for more info.

Each donation helps keep Simtropolis online, open and free!

Thank you for reading and enjoy the site!

More About STEX Collections