Создание динамически исполняемого кода с использованием пользовательских классов и методов с использованием рефлексии и компиляции во время выполнения в C # - PullRequest
0 голосов
/ 24 декабря 2018

Небольшой фон:

Моя цель - создать API в C #, который принимает два ввода:

  1. Два пользовательскихклассы, которые находятся в отдельном файле .cs и компилируются в определенном проекте.Этот проект создает, модифицирует и обычно выполняет любые манипуляции с объектами этих классов.Существует только два класса, потому что один отвечает за ввод пользовательского метода (см. Ниже), а другой - за его вывод
  2. Пользовательские методы, которые работают с объектами классов, упомянутых выше.Эти методы дают определенный результат - объект класса, отвечающий за вывод.

И производит один вывод:

  1. Объекткласс, отвечающий за вывод.

Вот пример моих классов ввода-вывода:

namespace DummyIOClasses
{
    #region IO_CLASSES
    public class InputClass
    {
        public InputClass(List<double> vals)
        {
            tubeValues = new List<double>();

            foreach (double val in vals)
            {
                tubeValues.Add(val);
            }
        }

        public List<double> tubeValues;
    }

    public class OutputClass
    {
        public OutputClass()
        {
            warningsCards = new List<string>();
            warningsTubes = new List<string>();
            result = "N/A";
        }

        public List<string> warningsCards;
        public List<string> warningsTubes;

        public string result;
    }
    #endregion
}

А вот пример пользовательского метода:

public OutputClass GetOutput(InputClass input)
{
    OutputClass output = new OutputClass();

    double sum = 0;

    foreach (double val in input.tubeValues)
    {
        if (val % 2 == 0)
        {
            output.warningsCards.Add("Divisible by 2!");
        }

        if (val % 3 == 0)
        {
            output.warningsTubes.Add("Divisible by 3!");
        }

        sum += val;
    }

    output.result = sum.ToString();

    return output;
}

Мой API должен загружать разные методы, которые работают с одним и тем же набором классов ввода-вывода.Но всякий раз, когда я хочу переключиться на другой набор классов ввода-вывода и использовать другие пользовательские методы, которые были созданы для этого нового набора, я должен иметь возможность делать это с помощью кода во время выполнения.

Это похоже напродвинули это, чтобы реализовать, и я все еще довольно плохо знаком с C # ... К сожалению, я даже не знаю, с чего начать и что делать, чтобы достичь своей цели.

Итак, мой вопрос: Как реализовать такой API?Я не прошу решения, я прошу более опытных людей направлять меня в правильном направлении.Какие инструменты C # я должен использовать для этой задачи и как?

Достигнутый прогресс

Я реализовал класс, который принимает фиктивный .cs файл с набором заполнителей для кода, который будет вставлен в будущем.Выглядит это так:

ASSEMBLY_PASTE

namespace ScriptInterpreter
{
    CLASS_PASTE

    public class Interpreter
    {
        METHOD_PASTE
    }
}

Как видите, есть 3 заполнителя.Один для `использования xxxxxxx; 'операторы, один для пользовательских классов ввода-вывода и один для пользовательского метода, который работает с объектами этих классов.

Первый заполнитель прост, я заполняю List<string> различных операторов использования во время выполнения, а затемвызовите метод, который заменяет заполнитель на эти операторы, довольно просто.

Что касается секундного заполнителя, я копирую код для классов ввода-вывода из отдельного файла .cs проектов (код заключен в определенные#region, чтобы сделать это проще) и вставьте их вместо этого заполнителя.

Третий заполнитель еще проще, у меня есть файл .cs, который никогда не компилируется и просто копируется в папку сборки,у него есть собственный метод, который работает с классами из моего проекта.Поэтому я просто копирую и вставляю весь файл вместо заполнителя.

После всех этих манипуляций файл результата выглядит следующим образом:

using System;
using System.Collections.Generic;

namespace ScriptInterpreter
{
    public class InputClass
    {
        public InputClass(List<double> vals)
        {
            tubeValues = new List<double>();

            foreach (double val in vals)
            {
                tubeValues.Add(val);
            }
        }

        public List<double> tubeValues;
    }

    public class OutputClass
    {
        public OutputClass()
        {
            warningsCards = new List<string>();
            warningsTubes = new List<string>();
            result = "N/A";
        }

        public List<string> warningsCards;
        public List<string> warningsTubes;

        public string result;
    }

    public class Interpreter
    {
        public OutputClass GetOutput(InputClass input)
        {
            OutputClass output = new OutputClass();

            double sum = 0;

            foreach (double val in input.tubeValues)
            {
                if (val % 2 == 0)
                {
                    output.warningsCards.Add("Divisible by 2!");
                }

                if (val % 3 == 0)
                {
                    output.warningsTubes.Add("Divisible by 3!");
                }

                sum += val;
            }

            output.result = sum.ToString();

            return output;
        }
    }
}

Затем я беру этот файл, помещаю его встроку, и используйте CSharpCodeProvier для компиляции сборки из нее.

Получив сборку, я просто вызываю метод GetOutput через нее с объектом пользовательского класса, которым я былс помощью.Вот так:

public object ExecuteMethod(object input)
{
    object compiledAssembly = results.CompiledAssembly.CreateInstance("ScriptInterpreter.Interpreter");

    MethodInfo methodInfo = compiledAssembly.GetType().GetMethod("GetOutput");

    object output = methodInfo.Invoke(compiledAssembly, new object[] { input });

    return output;
}

Но вот проблема!Типы разные!Хотя их поля и методы идентичны, в C # они отличаются, потому что они расположены в разных сборках и пространствах имен!

Итак, вот где я застрял прямо сейчас ... Я действительно не знаю, какинформировать эти две сборки о существовании друг друга или ... вы знаете ... заставить вещь работать!Я знаю, что может быть хакерское решение, например, преобразование всех данных в классе в строку и передача этой строки назад и вперед для создания объектов, но это последнее, что я хочу сделать, чтобы быть честным ...думаю, что должен быть лучший способ решения этой проблемы!

Если это возможно!

Я был бы очень признателен за вашу помощь с тем, что у меня уже есть.Потому что это именно тот API, который я планировал создать в первую очередь ...

Если вам это действительно интересно, вот полный код моего Interpreter класса.

И пример использование вместе с IO Classes из какого-либо проекта и пользовательский метод для этих классов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...