Динамическая реализация класса - PullRequest
0 голосов
/ 12 июня 2018

У меня есть классы, которые являются производными от базового класса, и я хотел бы создать экземпляры этих классов динамически во время выполнения.Используя следующий код, я могу построить класс, используя конструктор по умолчанию:

public abstract class Employee : IEmployeeInterface
{    
 public string Name { get; set; }

 public Employee() { }

 public Employee(string name)
 {
    Name = name;
 }    
}

public static class EmployeeBot
{
    static readonly Func<string, Func<Employee>> EmployeeBotFunction = x =>
    Expression.Lambda<Func<Employee>>(
    Expression.New(Type.GetType(x).GetConstructor(Type.EmptyTypes))
    ).Compile();

    public static Employee InstantiateNewEmployee(string type)
    {
        string argumentType = string.Format("{1}.{0}", type.ToString(), MethodBase.GetCurrentMethod().DeclaringType.Namespace);
        return EmployeeBotFunction(argumentType).Invoke();
    }
}

Worker реализует Employee и может быть создан с использованием:

Employee employee = EmployeeBot.InstantiateNewEmployee("Worker");

Тогда, так как Worker имеет все те же методы, что и Employee, вызов их даст ожидаемые результаты из рабочего класса.

Однако я не могу понять, как правильно реализовать подобный код для использования конструктора с аргументами.Например:

static readonly Func<string, Type[], Func<string, Employee>> NewEmployee = (x, y) =>
Expression.Lambda<Func<string, Employee>>(
Expression.New(Type.GetType(x).GetConstructor(y))
).Compile();

public static Employee InstantiateNewEmployee(string type, string Name)
{
 Type[] construct = new Type[] { typeof(string) }; 
 string argumentType = string.Format("{1}.{0}", type.ToString(), MethodBase.GetCurrentMethod().DeclaringType.Namespace);
 return NewEmployee(argumentType, construct).Invoke(Name);
}

Вызов этого метода вызывает исключение:

EmployeesTests.InstantiateEmployeeWithName выбросило исключение: System.InvalidOperationException: метод может быть вызван только для типа, для которого тип.IsGenericParameter имеет значение true.

Как можно изменить функцию NewEmployee, чтобы принять необходимые параметры для второго конструктора?

1 Ответ

0 голосов
/ 12 июня 2018

Я ответил на это с помощью @JSteward, так что спасибо!

В сочетании с другой версией Expression.New и исправлением глупой ошибки я смог решить эту проблему.Вы не можете создать экземпляр абстрактного класса, поэтому мой конструктор должен был находиться в одном из производных классов, например, public class Worker : Employee, а не public abstract class Employee.

Фактическая ошибка заключалась в том, что параметр конструктора для Expression.New(Type.GetType(x).GetConstructor(y)); был null потому что это не было определено на Рабочем.

Второй пример теперь становится;(хотя я уверен, что это можно улучшить!).

static readonly Func<string, Type[], ParameterExpression[], Func<string, Employee>> NewEmployee = (x, y, z) =>
Expression.Lambda<Func<string, Employee>>(
        Expression.New(Type.GetType(x).GetConstructor(y), z)
, z).Compile();

public static Employee InstantiateNewEmployee(string type, string Name)
{
 Type[] construct = new Type[] { typeof(string) };
 string argumentType = string.Format("{1}.{0}", type.ToString(), MethodBase.GetCurrentMethod().DeclaringType.Namespace);
 ParameterExpression[] arguments = new ParameterExpression[] { Expression.Parameter(typeof(string)) };
 return NewEmployee(argumentType, construct, arguments).Invoke(Name);
}

Пост из комментариев @JSteward на самом деле уже обобщает это требование: linq-expressions-creation-objects

Я ценю вашу помощь, а также комментарий из блога.

Но приятно видеть, что я не единственный человек, достаточно сумасшедший, чтобы использовать выражения таким образом!

...