Есть ли способ динамически выполнить строку в .net, аналогично eval () в javascript или динамический sql в sql? - PullRequest
8 голосов
/ 03 февраля 2009

Есть ли способ динамически выполнять код, содержащийся в строке, используя .net 2.0, аналогично eval () в javascript или с помощью sp_executeSQL в tsql?

У меня есть строковое значение в переменной, которым я хочу манипулировать в какой-то момент в моем приложении - поэтому код, по сути, будет манипулировать строками. Я не знаю, какие разные манипуляции понадобятся, поэтому я бы хотел, чтобы они были настраиваемыми.

Мне все равно, на каком языке написан динамический код, какой бы самый простой для реализации и достаточно простой для написания.

Например, я мог бы захотеть заменить экземпляры '.' символ с '-', или исключить все пробелы, или аналогичные. Если бы я делал это в SQL, я бы использовал динамический SQL, но я хочу выполнить его в коде .net, что-то вроде этого:

// Get the value to be manipulated
string s = ... // wherever s comes from

// Get the manipulation code, eg this might come from a database 
// setting that can be changed without recompiling the .net code.
string manipulation = Settings.GetSomeValue("ManipulationSetting");

// This is what I want to know how to do: apply some manipulation to the string.
string result = MagicDynamicEvalClass.Eval(manipulation, s);

// Now I would do stuff with the result.

Я мог бы просто использовать регулярные выражения находить / заменять выражения. Поскольку все, что я делаю - это манипулирование строками, этого должно быть достаточно, при условии, что я могу написать достаточно умные регулярные выражения. например:

// Get the value to be manipulated
string s = ... // wherever s comes from

// Get the code to use to manipulate s, eg this might come from a database 
// setting that can be changed without recompiling the .net code.
string findRegex = Settings.GetSomeValue("RegexPattern");
string replaceRegex = Settings.GetSomeValue("RegexReplace");

// This is what I want to know how to do: apply some manipulation to the string.
string result = Regex.Replace(s, findRegex, replaceRegex);

// Now I can do stuff with the result.

Но в некоторых случаях моё требование к манипуляции может превышать то, что возможно с регулярным выражением, или я могу захотеть применить несколько шагов, например, заменить '.' с '-', а также с пробелами. Возможно, я мог бы сохранить список регулярных выражений поиска / замены и перебрать их ... но у кого-нибудь есть лучшее предложение?

ОБНОВЛЕНИЕ - пример использования динамического sql

Мне не нужно решение, которое требует от меня заранее знать, какие манипуляции возможны, и я действительно ищу что-то простое. например, в sql я бы сделал что-то вроде этого:

declare @s nvarchar(1000)
declare @manipulation nvarchar(1000)
declare @result nvarchar(1000)

-- ... Get the values from wherever they come from

-- Execute the manipulation dynamically
EXEC sp_ExecuteSQL @stmt = @manipulation
    , @params = N'@s nvarchar(1000), @result nvarchar(1000) OUTPUT'
    , @s = @s, @result = @result OUTPUT

Тогда я мог бы поместить произвольный sql в мою @manipulation, что-то вроде этого SET @result = REPLACE (REPLACE (@s, '.', '-'), '', '')

Да, это потребовало бы от меня быть осторожным в отношении того, какие значения разрешено вводить в @manipulation, но это дало бы мне гибкость, которая мне нужна в будущем.

Подобный подход был бы возможен в javascript, я думаю, с использованием eval ().

UPDATE - пример использования элемента управления MSScript из .net:

Этот кажется возможным подходом, хотя, возможно, излишним для простого случая, с которым я хочу иметь дело. Он использует библиотеку Microsoft Script Control для разрешения выполнения произвольного VBScript.

Ответы [ 8 ]

13 голосов
/ 04 февраля 2009

Это не слишком сложно;) Я собрал небольшой пример. Это должно помочь вам решить, хотите ли вы использовать динамические сценарии .. или регулярные выражения.

Что вы можете сделать, это создать интерфейс в вашей сборке, который будет реализован вашим динамическим кодом:

namespace CompileScriptExample
{
  public interface IStringManipulator
  {
    string processString(string aString);
  }
}

Затем создайте класс ScriptRunner:

namespace CompileScriptExample
{ 
public class ScriptRunner
{

    public static string RunScript(string scriptCode, string scriptParameter)
    {

        CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();

        //configure parameters
        CompilerParameters parameters = new CompilerParameters();
        parameters.GenerateExecutable = false;
        parameters.GenerateInMemory = true;
        parameters.IncludeDebugInformation = false;
        string reference;
        // Set reference to current assembly - this reference is a hack for the example..
        reference = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        parameters.ReferencedAssemblies.Add(reference+"\\CompileScriptExample.exe");

        //compile
        CompilerResults results = provider.CompileAssemblyFromSource(parameters, new string[] { scriptCode });

        if (results.Errors.Count == 0)
        {
            IStringManipulator compiledScript=(IStringManipulator)FindInterface(results.CompiledAssembly, "IStringManipulator");
            return compiledScript.processString(scriptParameter);//run the script, pass the string param..
        }
        else
        {
            foreach(CompilerError anError in results.Errors)
            {
                MessageBox.Show(anError.ErrorText);
            }
            //handle compilation errors here
            //..use results.errors collection
            throw new Exception("Compilation error...");
        }
    }

    private static object FindInterface(Assembly anAssembly, string interfaceName)
    {
        // find our interface type..
        foreach (Type aType in anAssembly.GetTypes())
        {
            if (aType.GetInterface(interfaceName, true) != null)
                return anAssembly.CreateInstance(aType.FullName);
        }
        return null;
    }
}

}

Теперь все, что вам нужно сделать, это создать строку сценария с кодом, который реализует ваш интерфейс, например ..

string myScriptString=@"using CompileScriptExample;
public class MyStringManipulator : IStringManipulator
{
  public string processString(string aString)
  {
        return aString+aString;
  }
};

и затем .. в своем коде используйте ScriptRunner для обработки вашей строки с вашим динамическим кодом:

string processedString = ScriptRunner.RunScript(myScriptString, "hello");
4 голосов
/ 03 февраля 2009

Я знаю, что вы после C #, но у меня есть код для этого в VB. Вы можете легко перевести его, используя конвертер VB в C # от Developer Fusion. Я использовал его в проекте, чтобы позволить пользователям добавлять сложные вычисления в свое приложение во время выполнения. Он компилирует их VB-код в библиотеку в памяти и затем запускает код, возвращающий полученный результат. Это может быть довольно легко переназначено за то, что вы пытаетесь сделать.

Imports System.Reflection
Imports System.CodeDom.Compiler
Imports System.Text.RegularExpressions
Imports System.Math

Module Module1

  Function Evaluate(ByVal Expression As String, ByVal Args() As Object) As Object

    If Expression.Length > 0 Then

        'Replace each parameter in the calculation expression with the correct values
        Dim MatchStr = "{(\d+)}"
        Dim oMatches = Regex.Matches(Expression, MatchStr)
        If oMatches.Count > 0 Then
            Dim DistinctCount = (From m In oMatches _
                                 Select m.Value).Distinct.Count
            If DistinctCount = Args.Length Then
                For i = 0 To Args.Length - 1
                    Expression = Expression.Replace("{" & i & "}", Args(i))
                Next
            Else
                Throw New ArgumentException("Invalid number of parameters passed")
            End If
        End If

        Dim FuncName As String = "Eval" & Guid.NewGuid.ToString("N")
        Dim FuncString As String = "Imports System.Math" & vbCrLf & _
                                   "Namespace EvaluatorLibrary" & vbCrLf & _
                                   "  Class Evaluators" & vbCrLf & _
                                   "    Public Shared Function " & FuncName & "() As Double" & vbCrLf & _
                                   "      " & Expression & vbCrLf & _
                                   "    End Function" & vbCrLf & _
                                   "  End Class" & vbCrLf & _
                                   "End Namespace"

        'Tell the compiler what language was used
        Dim CodeProvider As CodeDomProvider = CodeDomProvider.CreateProvider("VB")

        'Set up our compiler options...
        Dim CompilerOptions As New CompilerParameters()
        With CompilerOptions
            .ReferencedAssemblies.Add("System.dll")
            .GenerateInMemory = True
            .TreatWarningsAsErrors = True
        End With

        'Compile the code that is to be evaluated
        Dim Results As CompilerResults = _
            CodeProvider.CompileAssemblyFromSource(CompilerOptions, FuncString)

        'Check there were no errors...
        If Results.Errors.Count > 0 Then
        Else
            'Run the code and return the value...
            Dim dynamicType As Type = Results.CompiledAssembly.GetType("EvaluatorLibrary.Evaluators")
            Dim methodInfo As MethodInfo = dynamicType.GetMethod(FuncName)
            Return methodInfo.Invoke(Nothing, Nothing)
        End If

    Else
        Return 0

    End If

    Return 0

  End Function

End Module

Я настроил свой динамический код так:

Dim Expr As String = "  If ({0} < 20000) Then" & vbCrLf & _
                     "    Return Max(15, Min(75,0.12*{0}))" & vbCrLf & _
                     "  Else" & vbCrLf & _
                     "    Return Max(75,0.05*{0})" & vbCrLf & _
                     "  End If"

А затем установите некоторые аргументы для выражения и выполните:

Dim Args As New List(Of String)
While True
    Dim Val As String = Console.ReadLine
    Args.Clear()
    If IsNumeric(Val) Then
        Args.Add(Val)
        Dim dblOut As Object = Evaluate(Expr, Args.ToArray)
        Console.WriteLine(dblOut)
    Else
        Exit While
    End If
End While
1 голос
/ 26 января 2011
1 голос
/ 03 февраля 2009

, как уже упоминалось, невозможно скомпилировать c # в функции eval (). эта функциональность запланирована для более позднего выпуска clr, который демонстрируется на PDC.

В качестве другого решения, если ваше приложение может работать в режиме моно, вы можете просто использовать его функцию eval, которая может динамически компилировать код на C #, как JavaScript. в основном он уже делает то, что сможет сделать .net через год или два.

в качестве альтернативы, если вы не можете использовать моно, вы можете написать часть, которая выполняет манипуляции со строками в ironruby, которая имеет eval (). остальная часть вашего кода даже не узнает, что вы используете ruby ​​для этого класса / assambly.

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

class MyDynamicEvalClass
  def eval(someString,transformString)
    eval(someString,transformString)
  end
end

и заменой «ManipulationSetting» на некоторый код ruby, который возвращает новую строку

1 голос
/ 03 февраля 2009

Я думаю, что для этого можно использовать рефлексию и кодирование, но это совсем не тривиально, и я советую против этого.

В качестве альтернативы вы можете попробовать настроить строку формата, возможно, в дополнение к регулярному выражению.

0 голосов
/ 10 июня 2011

Нет встроенного метода C # для вызова eval () во время выполнения.

Однако моя программа на C # eval позволяет оценивать код на C #. Он обеспечивает оценку кода C # во время выполнения и поддерживает множество операторов C #. На самом деле этот код можно использовать в любом проекте .NET, однако он ограничен использованием синтаксиса C #. Загляните на мой сайт, http://csharp -eval.com , для получения дополнительной информации.

0 голосов
/ 03 февраля 2009

На днях я наткнулся на что-то, что делает это на другом языке .NET: http://reverseblade.blogspot.com/2009/02/dont-wait-for-c-5-use-nemerle.html

Я полагаю, что вы можете написать свой код для обработки строк в nemerle, скомпилировать и ссылаться из своего приложения на C # или VB.

Я бы все же предпочел бы расширяемый дизайн, как предполагает casperOne. Выполняя динамические сценарии, вы будете просто вставлять компиляцию в приложение и развертывать в любом процессе, который доставит в приложение строки программирования. Но, похоже, у вас есть свои причины, поэтому единственное, что я хотел бы рассмотреть, это безопасность. В идеале вы хотите максимально ограничить пользователя просто манипулированием строками, и в этом случае List-of-Regex кажется довольно хорошим вариантом.

0 голосов
/ 03 февраля 2009

Хотя вы можете использовать перечисление для обозначения действия, которое вы хотите предпринять, или использовать CodeDom для динамического генерирования кода, все сводится к тому, что вы хотите определить какое-то преобразование, что означает, что у вас есть входные данные, и вывод.

Выяснить вывод легко в этом случае, у вас есть строка. Для входов может показаться, что вы можете иметь переменное количество входов. Это будет определено как IEnumerable<string>.

Имея это в виду, вы можете определить интерфейс следующим образом:

public interface IStringManipulation
{
  string Manipulate(IEnumerable<string> parameters);
}

Тогда было бы легко определить реализации этого типа и затем поместить имена типов в вашу конфигурацию.

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

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

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