tonerdo / pose

Replace any .NET method (including static and non-virtual) with a delegate
MIT License
1.07k stars 75 forks source link

Doesn't work in Unity 3D on Mac OS #54

Open zmorris opened 4 years ago

zmorris commented 4 years ago

Hi I tried the examples in Unity3D 2019.1.9f1 on Mac OS 10.13.6 under .NET 4.x player settings, using https://github.com/jbevain/mono.reflection for the using Mono.Reflection clauses in Pose, but I can't get anything at all to work:

public class MyClass
{
    public void DoSomething()
    {
        Debug.Log("DoSomething()");
    }
}

// try shimming class method:

MyClass myClass = new MyClass();

myClass.DoSomething();

Shim classShim = Shim.Replace(() => Is.A<MyClass>().DoSomething()).With(
                delegate(MyClass @this) { Debug.Log("doing something else with myClass"); });

myClass.DoSomething();

// try shimming instance method:

MyClass myClass = new MyClass();

myClass.DoSomething();

Shim myClassShim = Shim.Replace(() => myClass.DoSomething()).With(
                delegate(MyClass @this) { Debug.Log("doing something else with myClass"); });

myClass.DoSomething();

No matter what I try, I always see:

DoSomething()
DoSomething()

I'm wondering if this is due more to running on Mac OS than using Unity. Or perhaps that Mono.Reflection repo just doesn't work.

If nothing comes to mind, I can maybe put together an example Unity project that at least shows the Pose issue.


BONUS

Also while I have you here, do you know if Pose shims work on iOS and Android? It's looking like the technique of swapping the IL code might not work on mobile because they are sticklers about using immutable code only (at least for Harmony, which uses similar techniques):

https://github.com/pardeike/Harmony/issues/196

I can maybe use https://github.com/jbevain/cecil but it might have similar issues.

I'm trying to extend a sealed class so that I can have a delegate fire whenever a class instance method is called or a property is changed, in order to implement reactive programming techniques. Without that functionality, I'll have no choice but to tell users to remember to call MyClass.SomethingChanged(sealedClass), which isn't future-proof so will inevitably lead to bugs, especially when new developers use the class and forget to call that. So far I've wasted upwards of a week chasing this, and it's been one of the first great disappointments I've encountered in C#.

I tried building a dynamic class with https://github.com/meziantou/Meziantou.Framework/blob/master/src/Meziantou.Framework/ReflectionDynamicObject.cs to pass any method calls through to the sealed class, but I was unable to get the implicit cast operator working, so I couldn't pass the dynamic class off as the original type back to Unity (it caused a stack overflow trying to return and cast itself infinitely). Which means that users would have to cast the sealed class to the dynamic class themselves which defeats the whole purpose.

This is a really big problem that severely limits Unity to cookie cutter solutions since we can't override builtin functionality. Of course most people have no idea what I'm talking about, so just tell me I'm doing it wrong. I'm hoping someone with your depth of understanding on these issues might have an idea for how to override/inherit sealed class methods or add INotifyPropertyChanged or shim a sealed class instance, and be able to run that type of code in production.


Thanks!

Kdoje commented 4 years ago

I'm having a similar problem where I want to reroute attachments to a delegate in a sealed class. I think part of your problem is that you don't use PoseContext.Isolate(() => { myClass .DoSomething(); }, classShim);

This will change the body of the method, but I've had no luck changing the return values of properties yet.