3

The Question: Is this possible to clone a class definition using reflection? I am not talking about shallow cloning nor deep cloning. I am talking about definition cloning. I want to have a class with a static variable not shared between all instances, but just the definition I created. And I (or the library) need to be able to create an instance from this class later on.

The Problem: You see, I need this because of the following scenario,

There is this library that expects me to provides it with a type having a certain static method. However in my case this static method needs to compare two value one from a non static field of another type. This makes it impossible to pass the instance that has the information to the class because it is not yet initialized. Check out the following example of the situation:

class MasterClass 
{
    public int SomeInfo {get; set;} = 10;
    public void PeresentClass()
    {
        SOMELIBRARY.RegisterType(typeof(StaticClass));
    }
}
class StaticClass
{
    public static bool CanCreate(int someVar)
    {
        // I need to compare someVar with the SomeInfo property of MasterClass instance that presented this type to the SOMELIBRARY.
    }
    public StaticClass()
    {
        // Something irrelevant
    }
}

In the above example I have no control over the SOMELIBRARY and the way they decided to write the code. But it seems that they some how want to call the CanCreate method first and then create an instance of class if it meets the requirements.

However, for CanCreate to works correctly I need to have access to the instance of the class presented the StaticClass to the SOMELIBRARY in first place. And I cant make MasterClass static because there are more than one instance of this class active at every time.

Only way I could think of was to redefine a new StaticClass with a static field pointing to the MasterClass that defined it (or cloned the definition). However my knowledge of reflection failed me to do so yet. So here I am asking it this is even possible? And I really would like to be able to do it under PCL profiles.

Real World: Just for more information, I am actually talking about XAMARIN.iOS and NSUrlProtocol class, specially CanInitWithRequest method.

Possible Solution: With more thinking, I found that another way to solve this problem is to make the StaticClass generic; Doing so allows me to have a static variable per type definition. However, for this to work I need to be able to create unique and possibly empty types at run-time. Is this possible?

XAMARIN.iOS: Unfortunately Reflection.Emit is not available on iOS so for now I don't believe that this is even possible in any way. Still waiting for your comments on the situation tho.

https://developer.xamarin.com/guides/ios/advanced_topics/limitations/#System.Reflection.Emit

Soroush Falahati
  • 2,196
  • 1
  • 27
  • 38
  • What SOMELIBRARY does exactly with that StaticClass? – Evk Oct 01 '16 at 16:32
  • @Evk: It tries calling the `CanCreate` method before creating a new instance of the class to see if the class can handle that specific situation. – Soroush Falahati Oct 01 '16 at 16:35
  • And why CanCreate is static method? That's the requirement of that some library? – Evk Oct 01 '16 at 16:37
  • @Evk, Unfortunately, yes. https://developer.apple.com/reference/foundation/nsurlprotocol/1411389-caninitwithrequest?language=objc – Soroush Falahati Oct 01 '16 at 16:39
  • What if, before registering StaticClass, you will set current MasterClass to some static variable? For example, MasterClass.Current = this; SOMELIBRARY.RegisterType(typeof(StaticClass));? – Evk Oct 01 '16 at 16:47
  • @Evk, Well the problem is here that there are more than one instance of MasterClass and they might all try to register the StaticClass and claim its functionality for themselves. – Soroush Falahati Oct 01 '16 at 16:52
  • @Evk, You see, every instance of MasterClass needs to be able to work individually, or I could simply make the MasterClass static. But its not possible. – Soroush Falahati Oct 01 '16 at 16:53

2 Answers2

2

There are a number of ways to create a class at runtime, which seems like what you are asking. Your question seems to have ruled out System.Reflection.Emit, so you might want to explore some of the other answers on this topic to see if they may be suitable for your platform (Xamarin.IOS).

That said, your question seems to indicate a code smell in your implementation. You are trying to map a class instance across the API registration function, which relies on a static method to indicate a resource's suitability for handling a type of request (canInitWithRequest). This function should only indicate if the registered NSURLProtocol class is capable of handling a particular request type, it likely should not be dependent on some class property of another object in the system.

A better approach might be to have your NSURLProtocol instance look up the shared resource at runtime when it is invoked by the underlying framework. For instance, something like the following:

static class SystemMap {
    // Store some mapping information in a commonly accessible system resource
    // In this case a simple static class that wraps up a dictionary
    static Dictionary<Type, Master> systemMap = new Dictionary<Type, Master>();

    // Allow registered components to be accessed
    public static Master getRegisteredMaster(Type handlerType) {
        return systemMap[handlerType];
    }

    // Allow new registrations to be made in your system
    public static void registerNewMaster(Master registrant, Type handlerType) {
        systemMap[handlerType] = registrant;
    }
}

class Master {
    // This would be your custom class that you instantiate throughout your system
    public string name;
    public int someVar { get; set; } = new Random().Next(1, 100);
    public Master(string name) {
        this.name = name;
    }
}

class BaseHandlerType {
    // This would be NSURLProtocol
}

class Handler1 : BaseHandlerType {
    // This would be canInitWithRequest
    public static bool CanCreate(int someVar) {
        Master myMaster = SystemMap.getRegisteredMaster(typeof(Handler1));
        return someVar > myMaster.someVar;
    }
}

class Handler2 : BaseHandlerType {
    //... Register various handler types to various "Master" instances in your system
    // This is a concrete implementation of NSURLProtocol
}

class Handler3 : BaseHandlerType {
    //... Register various handler types to various "Master" instances in your system
    // This is a concrete implementation of NSURLProtocol
}

class SystemFactory {
    // Use a factory method to instantiate the system components and plug things together
    public void initializeSystem() {
        var masterA = new Master("a");
        var masterB = new Master("b");
        var masterC = new Master("c");
        SystemMap.registerNewMaster(masterA, typeof(Handler1));
        SystemMap.registerNewMaster(masterB, typeof(Handler2));
        SystemMap.registerNewMaster(masterC, typeof(Handler3));
        SomeLibrary.register(typeof(Handler1));
        SomeLibrary.register(typeof(Handler2));
        SomeLibrary.register(typeof(Handler3));
    }
}

static class SomeLibrary {
    public static void register(Type handlerType) {
        // This represents the API registration
    }
}

This pattern might help you establish the relationship between components that you are trying to achieve via class creation at runtime. This pattern would allow your various different types of handlers (i.e. NSURLProtocol classes) to access a different Master instance when they are invoked. In this example, masterA maps to Handler1, masterB to Handler2, and so on.

Kin3TiX
  • 708
  • 1
  • 5
  • 18
  • [Sorry for poor english] Thank you for the answer, but in this case, the Handlers classes must be defined at design time. I was searching for a solution which I can create at running time. Went I clear enough? – Diego Rafael Souza Jul 06 '17 at 19:40
  • Thanks for the answer, after a long time :), Unfortunately, there seems to be no way to define classes at the runtime with XAMARIN.iOS. And even tho it seems like a code smell, but this is more of an incompatibility in indented functionalities. Apple designed the API to handle request through a static request handler, however, my implementation needed an instance based check. Unfortunately, in your example, you followed the same way as Apple and defined classes beforehand. I don't know how many class I might need, one, two or a hundred. So I can't define them in the code. – Soroush Falahati Jul 07 '17 at 11:59
0

After a lot of searching, I found no way to create even an empty type in C# under Xamarin.iOS and so as the result, I had to change my code to fit the expectations of the Apple API.

In my case, I ended up keeping a list of all instances of MasterClass to use in the StaticClass; both the constructor and the static method go throghe the list and match the desired MasterClass with the request. There is a risk here that by doing so you end up having a memory leak because instances of MasterClass will never get collected, but in my case, this wasn't a concern.

Soroush Falahati
  • 2,196
  • 1
  • 27
  • 38