Search code examples
c#genericsreflectionactivator

C# dynamically load generic class with dynamic type parameters per strings and strongly typed result


I am attempting to take a JSON file that contains data about a generic class (assembly, class name, type parameter names), and dynamically load and instantiate the specified class. I can do this if I use interfaces for the type parameter. However, implementation specifics won't allow me to go this route, so I need to use the specific type parameter classes.

I have the following code so far:

var ci = Invoke.GetClassRef(@"D:\ROCT\Debug\x64 Core\Common Complex Functions.dll", "Namespace.Class");
Type[] typeArgs = { typeof(DoubleABCAry), typeof(FloatABCAry), typeof(Search3Range), typeof(object) };
var _model = ci.GetType().MakeGenericType(typeArgs);
var model = Activator.CreateInstance(_model) as ComplexBaseModel<IModelInput, IModelIOBase, ISearchDimension, object>;
model.Validate();

Line 1: loads the assembly and class. No problem there.

Line 2: lists the types that I need to specify name-wise via a string[].

Line 4: DoubleABCAry implements IModelInput; FloatABCAry implements IModelIOBase, etc.

Line 4 is casting the object to its base definition (shown), which is not what I want. I want RGB255ToCIELAB : ComplexBaseModel<DoubleABCAry, FloatABCAry, Search3Range, object>.

Line 5 shows that the class is correctly understood. However, the class clearly is implementing interfaces, not the specific types I need.

Is there a way to achieve these two objectives:

  1. Use string names to specify each type parameter.
  2. Ultimately have model strongly typed as explained?

I know it's achievable with dynamically compiled code, but that is my last resort.

I am using C# 9 preview.


Solution

  • So i think ultimately your problem boils down to the fact that you want to load dynamic types but then treat them as static such that you can invoke methods using instance.MyMethod() syntax. This is not possible (AFAIC) simply because instance.MyMethod() is compiled down to IL and at that point compiler does not know what instance is.

    So provided you do not have any way of knowing what is the method you want to invoke, you have to go full dynamic route, i.e. use reflection APIs to invoke the right method.

    So you would need to supply not just class name but also method name or have a convention e.g. always invoke method called 'Validate' or something similar.

    A simple listing below illustrates my point:

    using System;
    using System.Reflection;
    
    namespace Shoes {
        
        public interface IA { }
        public class A : IA {}
    
        public interface IB { }
        public class B : IB {}
    
        public class SomeKindOfModel<T1, T2> { 
    
        }
    
        public class ConcreteModel<T1, T2> : SomeKindOfModel<T1, T2> {
           public bool Validate() {
               return true;
           }    
        }
    
        public class Program
        {   
            public static void Main()
            {
                // since i don't have your Invoke.GetClassRef, i just did this, works in .net core
                var type = Type.GetType($"Shoes.ConcreteModel`2");
    
                var genType = type.MakeGenericType(new[] { typeof(A), typeof(B) });
                var instance = Activator.CreateInstance(genType);
                var result = genType.GetMethod("Validate", BindingFlags.Instance | BindingFlags.Public)
                    .Invoke(instance, null); // instead of null you can pass data etc.
                Console.WriteLine(result); // prints True
            }
        }
    }