Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Research using an interface with a static abstract Create method for NSObject/INativeObject to avoid reflection #18327

Closed
rolfbjarne opened this issue May 24, 2023 · 2 comments
Labels
enhancement The issue or pull request is an enhancement performance If an issue or pull request is related to performance
Milestone

Comments

@rolfbjarne
Copy link
Member

rolfbjarne commented May 24, 2023

Look into whether we can use an interface with a static abstract Create method to avoid reflection in Runtime.GetNSObject and Runtime.GetINativeObject:

using System;
using System.Reflection;

#nullable enable

namespace ObjCRuntime {

    interface INSObjectCreator<TSelf>
        where TSelf : INSObjectCreator<TSelf>?
    {
        static virtual TSelf? Create (IntPtr handle)
        {
            // Default reflection-based implementation?
            var ctor = typeof (TSelf).GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, new Type[] { typeof (IntPtr) });
            if (ctor is not null)
                return (TSelf) ctor.Invoke (new object[] { handle });
            return default;
        }
    }

    class MyClass1 : NSObject, INSObjectCreator<MyClass1>
    {
        protected MyClass1 (IntPtr handle) : base (handle) {}
        static MyClass1 INSObjectCreator<MyClass1>.Create (IntPtr handle) => new MyClass1 (handle);
    }

    class MyClass2 : NSObject
    {
        protected MyClass2 (IntPtr handle) : base (handle) {}
    }

    interface INativeObjectCreator<TSelf>
        where TSelf : INativeObjectCreator<TSelf>?
    {
        static virtual TSelf? Create (IntPtr handle, bool owns)
        {
            // Default reflection-based implementation?
            var ctor = typeof (TSelf).GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, new Type[] { typeof (IntPtr), typeof (bool) });
            if (ctor is not null)
                return (TSelf) ctor.Invoke (new object[] { handle, owns });
            return default;
        }
    }

    interface INativeObject {}
    class NSObject : INSObjectCreator<NSObject>
    {
        protected NSObject (IntPtr handle) {}
    }

    class Selector : INativeObject, INativeObjectCreator<Selector>
    {
        public Selector (IntPtr handle, bool owns) { }
    }

    class Class : INativeObject, INativeObjectCreator<Class>
    {
        private Class (IntPtr handle, bool owns) { }
        static Class INativeObjectCreator<Class>.Create (IntPtr handle, bool owns) => new Class( handle, owns);
    }

    class Runtime {
        public static T? GetINativeObject<T> (IntPtr handle, bool owns) where T: class, INativeObject, INativeObjectCreator<T>
        {
            return T.Create (handle, owns);
        }

        public static T? GetNSObject<T> (IntPtr handle) where T: NSObject
        {
            var ctor = typeof (T).GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, new Type[] { typeof (IntPtr) });
            if (ctor is not null)
                return (T) ctor.Invoke (new object[] { handle });
            return default;
        }

        public static T? GetNSObject2<T> (IntPtr handle) where T: NSObject, INSObjectCreator<T>
        {
            return T.Create (handle);
        }
    }

    public static void Main (string[] args)
    {
        Console.WriteLine (Runtime.GetINativeObject<Selector> (IntPtr.Zero, false));
        Console.WriteLine (Runtime.GetNSObject2<MyClass1> (IntPtr.Zero));
        Console.WriteLine (Runtime.GetNSObject<MyClass2> (IntPtr.Zero));
        Console.WriteLine (Runtime.GetNSObject2<NSObject> (IntPtr.Zero));
    }
}

Ref: dotnet/runtime#86649
Ref: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members

@rolfbjarne rolfbjarne added enhancement The issue or pull request is an enhancement performance If an issue or pull request is related to performance labels May 24, 2023
@rolfbjarne rolfbjarne added this to the .NET 8 milestone May 24, 2023
@rolfbjarne
Copy link
Member Author

@simonrozsival isn't this what you implemented already?

@rolfbjarne rolfbjarne modified the milestones: .NET 8, .NET 9 Sep 11, 2023
@simonrozsival
Copy link
Contributor

@rolfbjarne yes, I think this was implemented in #18519. We closed a different issue with that PR - #18358. There seems to be quite a lot of overlap between those issues and I think that PR actually covered both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement The issue or pull request is an enhancement performance If an issue or pull request is related to performance
Projects
Status: Done
Development

No branches or pull requests

2 participants