mardi 29 octobre 2019

How to call constructor method with generic type not known at compile time

Platform: .NET framework 4.5.1, C#

I am attempting to call the constructor for a class, not knowing the generic type at run-time and wondering how I could do this. Based on other answers I have read, it’s clear that I need to rely on Reflection.

I have tried a method that includes me getting the MethodInfo array of the constructors and use that, however I am still struggling to put everything together. Below my classes.

I have a parent abstract class, defined like below.

 public abstract class ParentClass {
   public string     name  {get;}
   public CustomEnum type  {get;}
   public object     value {get;}

   public ParentClass(string name, CustomEnum type, object value = null){
      this.name  = name;
      this.type  = type;
      this.value = value;  
   }
} 

One of the many implementations of the abstract class depends on a GenericType, defined like below.

 public class ChildClass<GenericType> : ParentClass {
    public  delegate GenericType FunctionPointer(ExternalObject object);

    public  override string            name   {get;}
    public  override CustomEnum        type   {get;}

    public           FunctionPointer   mappingFunction = null;

    private          ExternalObject    _value {get;} 
    private          List<GenericType> mappedValues = null;
    private override object     value  {get => getValue(); set => setValue(value); }


    public ChildClass(string name, CustomEnum type, ExternalObject value = null, FunctionPointer mappingFunction = null){
       this.name            = name;
       this.type            = type;
       this._value          = value;
       this.mappingFunction = mappingFunction;
       populateList(value);
    }


    private void populateList(ExternalObject value){
       if(this.mappingFunction != null){
          if(value != null){
            this.mappedValues = new List<GenericType>();
            foreach(var item in value){
                GenericType valueToAdd = mappingFunction.Invoke(value);
                this.mappedValues.Add(valueToAdd);
            }
          }
       }
    }


    private object getValue(){
       if(this.mappingFunction == null){
          return this._value;
       }
       else {
          return this.mappedValues;
       }
    }


    private void setValue(object value){
       this._value = value;
       if(this.mappingFunction != null){
          populateList(this._value);
       }
    }



 }

This is how I have the process code set up:

 public ParentClass[] getValues(ParentClass[] inputParameters){
      ParentClass[] outputParameters = null;
      if(inputParameters != null){
         List<ParentClass> outputList = new List<ParentClass>();
         foreach(ParentClass inputParameter in inputParameters){
             object value = obtainValue(inputParameter);
             ParentClass populatedParameter = populateParameter(inputParameter, value);
             outputList.Add(populatedParameter);
         }
         outputParameters = outputList.ToArray<ParentClass>();
      }
      return outputParameters;
 }

The method I am specifically trying to code for, is as follows:

 private ParentClass populateParameter(ParentClass inputParameter, object value){
     ParentClass populatedParameter = null;
     if(inputParameter != null){
        switch(inputParameter.type){
            case CustomEnum.WITHOUT_GENERIC_TYPE:
                 [...]
                 break;
            case CustomEnum.WITH_GENERIC_TYPE:
                populatedParameter = populateGenericParameter(inputParameter, (ExternalObject) value);
                break;
        }
     }
     return populatedParameter;
 }

I am stuck trying to understand how to craft the below method:

private ParentClass populateGenericParameter(ParentClass inputParameter, ExternalObject value) {
     ParentClass populatedParameter = null;
     FieldInfo   functionPointer    = null;

     Type genericType = typeof(ChildClass<>).MakeGenericType(new object[] { inputParameter.getType() });

     FieldInfo[] parameterFields = genericType.GetConstructors();

     if(parameterFields != null){
        const string functionPointerString = "FunctionPointer";
        foreach(FieldInfo parameterField in parameterFields){
            if(parameterField.ToString().ToUpper().Contains(functionPointerString.ToUpper()) == true){
            functionPointer = parameterField; 
            }
        }
     }

     ConstructorInfo[] allConstructors = genericType.GetConstructors();

     if(allConstructors != null && allConstructor.Length > 0){
        ConstructorInfo constructor = allConstructor[0];
        object[] constructorParameters = new object[] {parameter.name, parameter.type, value, functionPointer.GetValue(null) };


        populatedParameter = (ChildClass<>) constructor.Invoke(constructorParameters);

     }

     return populatedParameter;
}

So, the problem is with the constructorParameters object and the Invoke method.

The "functionPointer.GetValue(null)" throws an exception.

The casting of ChildClass when invoking the constructor is a compile-time error, and so far the only way to get around it has been to replace it with the ParentClass.

I am new to reflection, so looking forward to read how to get around this problem. I have visited other topics as well, and that has led me to try to use the ConstructorInfo and FieldInfo arrays. I seem stuck here though. I hope what I am trying to achieve is possible.





Aucun commentaire:

Enregistrer un commentaire