C # 코드를 동적으로 평가하려면 어떻게해야합니까?


92

eval("something()");JavaScript에서 코드를 동적으로 실행할 수 있습니다 . C #에서 동일한 작업을 수행 할 수있는 방법이 있습니까?

내가하려는 작업의 예는 다음과 같습니다. 정수 변수 (예 i:)가 있고 "Property1", "Property2", "Property3"등의 이름으로 여러 속성이 있습니다. 이제 몇 가지 작업을 수행하고 싶습니다. 의 값에 따라 "속성 i "속성에 i.

이것은 Javascript로 정말 간단합니다. C #으로이 작업을 수행 할 수있는 방법이 있습니까?



2
c #은 ironpython의 평가를 호출합니다. C # 4.0에서 시도했습니다. C # 2.0 사용 경험 없음
Peter Long

@Peter Long, IronPython의 평가판에 대한 문서는 어디에서 찾을 수 있습니까?
smartcaveman 2011-06-16

@AdhipGupta이 Q & A가 꽤 오래되었다는 것을 알고 있지만 Davide Icardi의 답변에 제공된 설명과 매우 흡사하게 들리는 비디오 재생 목록 을 방금 출시했습니다 . 그것은 극적으로 다르며 아마도 확인해 볼 가치가 있습니다.
Rick Riggs 19 년

답변:


49

불행히도 C #은 이와 같은 동적 언어가 아닙니다.

그러나 할 수있는 일은 클래스와 모든 것이 포함 된 C # 소스 코드 파일을 만들고 C # 용 CodeDom 공급자를 통해 실행하고 어셈블리로 컴파일 한 다음 실행하는 것입니다.

MSDN의이 포럼 게시물에는 페이지 아래에 몇 가지 예제 코드가 포함 된 답변이 포함되어 있습니다
. 문자열에서 익명 메서드를 만드시겠습니까?

나는 이것이 매우 좋은 해결책이라고 거의 말하지 않지만 어쨌든 가능합니다.

그 문자열에서 어떤 종류의 코드를 기대합니까? 예를 들어 수학 표현식과 같이 유효한 코드의 사소한 하위 집합 인 경우 다른 대안이있을 수 있습니다.


편집 : 글쎄요, 먼저 질문을 철저히 읽도록 가르쳐줍니다. 예, 반성하면 여기서 도움을 줄 수 있습니다.

문자열을; 먼저 개별 속성을 가져 오려면 다음 코드를 사용하여 클래스의 특정 속성에 대한 PropertyInfo 개체를 가져온 다음 해당 개체를 사용하여 특정 개체를 조작 할 수 있습니다.

String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);

링크 : PropertyInfo.SetValue 메서드


GetProperty가 x, y 매개 변수가있는 함수 여야하는 경우?는 Text (1,2)를 의미합니까?
user1735921

@ user1735921 그런 다음 GetMethod(methodName)대신 사용 하고 매개 변수 값을 구문 분석하고 리플렉션을 사용하여 메서드를 호출해야합니다.
Lasse V. Karlsen

typeof (ObjectType)은 someObject.GetType ()과 유사합니다.
Thiago

34

Roslyn 스크립팅 API 사용 ( 여기에 더 많은 샘플 참조 ) :

// add NuGet package 'Microsoft.CodeAnalysis.Scripting'
using Microsoft.CodeAnalysis.CSharp.Scripting;

await CSharpScript.EvaluateAsync("System.Math.Pow(2, 4)") // returns 16

모든 코드를 실행할 수도 있습니다.

var script = await CSharpScript.RunAsync(@"
                class MyClass
                { 
                    public void Print() => System.Console.WriteLine(1);
                }")

그리고 이전 실행에서 생성 된 코드를 참조하십시오.

await script.ContinueWithAsync("new MyClass().Print();");

14

별로. 리플렉션을 사용하여 원하는 것을 얻을 수 있지만 Javascript만큼 간단하지는 않습니다. 예를 들어 객체의 private 필드를 무언가로 설정하려면 다음 함수를 사용할 수 있습니다.

protected static void SetField(object o, string fieldName, object value)
{
   FieldInfo field = o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
   field.SetValue(o, value);
}

11

이것은 c #의 평가 함수입니다. 문자열에서 익명 함수 (Lambda 표현식)를 변환하는 데 사용했습니다. 출처 : http://www.codeproject.com/KB/cs/evalcscode.aspx

public static object Eval(string sCSCode) {

  CSharpCodeProvider c = new CSharpCodeProvider();
  ICodeCompiler icc = c.CreateCompiler();
  CompilerParameters cp = new CompilerParameters();

  cp.ReferencedAssemblies.Add("system.dll");
  cp.ReferencedAssemblies.Add("system.xml.dll");
  cp.ReferencedAssemblies.Add("system.data.dll");
  cp.ReferencedAssemblies.Add("system.windows.forms.dll");
  cp.ReferencedAssemblies.Add("system.drawing.dll");

  cp.CompilerOptions = "/t:library";
  cp.GenerateInMemory = true;

  StringBuilder sb = new StringBuilder("");
  sb.Append("using System;\n" );
  sb.Append("using System.Xml;\n");
  sb.Append("using System.Data;\n");
  sb.Append("using System.Data.SqlClient;\n");
  sb.Append("using System.Windows.Forms;\n");
  sb.Append("using System.Drawing;\n");

  sb.Append("namespace CSCodeEvaler{ \n");
  sb.Append("public class CSCodeEvaler{ \n");
  sb.Append("public object EvalCode(){\n");
  sb.Append("return "+sCSCode+"; \n");
  sb.Append("} \n");
  sb.Append("} \n");
  sb.Append("}\n");

  CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
  if( cr.Errors.Count > 0 ){
      MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText, 
         "Error evaluating cs code", MessageBoxButtons.OK, 
         MessageBoxIcon.Error );
      return null;
  }

  System.Reflection.Assembly a = cr.CompiledAssembly;
  object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");

  Type t = o.GetType();
  MethodInfo mi = t.GetMethod("EvalCode");

  object s = mi.Invoke(o, null);
  return s;

}

1
@sehe Whoops, 오타를 수정했습니다 (Lambada => Lambda). 나는 그 노래가 Lambada라는 것을 몰랐기 때문에 이것은 의도하지 않았습니다. ;)
Largo

이 답변이 왜 투표를 덜 받는지 이해할 수 없었습니다. 매우 유용합니다.
Muzaffer Galata

9

C # 구문을 사용하여 작성된 텍스트 식을 대리자 (또는 식 트리)로 변환 할 수 있는 오픈 소스 프로젝트 Dynamic Expresso를 작성했습니다. 표현식은 컴파일이나 리플렉션을 사용하지 않고 구문 분석되고 표현식 트리 로 변환됩니다 .

다음과 같이 작성할 수 있습니다.

var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");

또는

var interpreter = new Interpreter()
                      .SetVariable("service", new ServiceExample());

string expression = "x > 4 ? service.SomeMethod() : service.AnotherMethod()";

Lambda parsedExpression = interpreter.Parse(expression, 
                          new Parameter("x", typeof(int)));

parsedExpression.Invoke(5);

내 작업은 Scott Gu 기사 http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx를 기반으로 합니다.


7

그 모든 것이 확실히 작동 할 것입니다. 개인적으로 그 특정 문제에 대해서는 아마도 조금 다른 접근 방식을 취할 것입니다. 아마도 다음과 같습니다.

class MyClass {
  public Point point1, point2, point3;

  private Point[] points;

  public MyClass() {
    //...
    this.points = new Point[] {point1, point2, point3};
  }

  public void DoSomethingWith(int i) {
    Point target = this.points[i+1];
    // do stuff to target
  }
}

이와 같은 패턴을 사용할 때 데이터가 값이 아닌 참조로 저장된다는 점에주의해야합니다. 즉, 프리미티브로이 작업을 수행하지 마십시오. 당신은 그들의 큰 부풀린 클래스 대응 물을 사용해야합니다.

나는 그것이 정확히 질문이 아니라는 것을 깨달았지만 그 질문에 대한 대답은 꽤 잘되어 있었고 대안적인 접근법이 도움이 될 것이라고 생각했습니다.


5

C # 문을 절대적으로 실행하려면 지금은 아니지만 C # 2.0에서 이미 Javascript 문을 실행할 수 있습니다. 오픈 소스 라이브러리 Jint 가 할 수 있습니다. .NET 용 자바 스크립트 인터프리터입니다. 자바 스크립트 프로그램을 전달하면 애플리케이션 내에서 실행됩니다. C # 개체를 인수로 전달하고 자동화 할 수도 있습니다.

또한 속성에 대한 표현식을 평가하려는 경우 NCalc를 시도 하십시오 .


3

리플렉션을 사용하여 속성을 가져오고 호출 할 수 있습니다. 이 같은:

object result = theObject.GetType().GetProperty("Property" + i).GetValue(theObject, null);

즉, 속성이있는 객체를 "theObject"라고 가정합니다. :)


2

웹 브라우저를 구현 한 다음 javascript가 포함 된 html 파일을로드 할 수도 있습니다.

그런 다음 document.InvokeScript이 브라우저 에서 방법으로 이동 합니다. eval 함수의 반환 값은 포착하여 필요한 모든 것으로 변환 할 수 있습니다.

여러 프로젝트에서이 작업을 수행했으며 완벽하게 작동합니다.

도움이되기를 바랍니다.


0

프로토 타입 함수로 수행 할 수 있습니다.

void something(int i, string P1) {
    something(i, P1, String.Empty);
}

void something(int i, string P1, string P2) {
    something(i, P1, P2, String.Empty);
}

void something(int i, string P1, string P2, string P3) {
    something(i, P1, P2, P3, String.Empty);
}

등등...



0

코드를 동적으로 컴파일하고 실행하는 작업을 단순화하기 위해 SharpByte.Dynamic 패키지를 작성했습니다 . 여기에 자세히 설명 된대로 확장 메서드를 사용하여 모든 컨텍스트 개체에서 코드를 호출 할 수 있습니다 .

예를 들면

someObject.Evaluate<int>("6 / {{{0}}}", 3))

3을 반환합니다.

someObject.Evaluate("this.ToString()"))

컨텍스트 객체의 문자열 표현을 반환합니다.

someObject.Execute(@
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");

이러한 명령문을 스크립트 등으로 실행합니다.

실행 파일은 여기 예제 에서 볼 수 있듯이 팩토리 메서드를 사용하여 쉽게 얻을 수 있습니다. 필요한 것은 소스 코드와 예상되는 명명 된 매개 변수 목록입니다 (토큰은 {{{0}}과 같은 삼중 괄호 표기법을 사용하여 포함됩니다). }, string.Format () 및 Handlebars와 유사한 구문과의 충돌을 피하기 위해 :

IExecutable executable = ExecutableFactory.Default.GetExecutable(executableType, sourceCode, parameterNames, addedNamespaces);

각 실행 가능 객체 (스크립트 또는 표현식)는 스레드로부터 안전하고 저장 및 재사용 할 수 있으며 스크립트 내에서 로깅을 지원하고 타이밍 정보와 마지막 예외가 발생하는 경우 저장하는 등의 작업을 수행 할 수 있습니다. 또한 각 실행 파일에 대해 컴파일 된 Copy () 메서드가 있습니다. 저렴한 복사본 생성, 즉 스크립트 나 표현식에서 컴파일 된 실행 가능 객체를 템플릿으로 사용하여 다른 사람을 생성합니다.

이미 컴파일 된 스크립트 또는 명령문을 실행하는 오버 헤드는 보통 하드웨어에서 1 마이크로 초 미만으로 비교적 낮으며, 이미 컴파일 된 스크립트와 표현식은 재사용을 위해 캐시됩니다.


0

이름으로 구조 (클래스) 멤버의 값을 얻으려고했습니다. 구조는 동적이지 않았습니다. 마침내 얻을 때까지 모든 답변이 작동하지 않았습니다.

public static object GetPropertyValue(object instance, string memberName)
{
    return instance.GetType().GetField(memberName).GetValue(instance);
}

이 메서드는 이름으로 멤버의 값을 반환합니다. 정규 구조 (클래스)에서 작동합니다.


0

Heleonix.Reflection 라이브러리를 확인할 수 있습니다 . 중첩 된 멤버를 포함하여 동적으로 멤버를 가져 오거나 설정 / 호출하는 메서드를 제공합니다. 멤버가 명확하게 정의 된 경우 리플렉션보다 빠른 getter / setter (대리자로 컴파일 된 람다)를 만들 수 있습니다.

var success = Reflector.Set(instance, null, $"Property{i}", value);

또는 속성의 수가 무한하지 않은 경우 setter를 생성하고 chache 할 수 있습니다 (setter는 컴파일 된 델리게이트이기 때문에 더 빠름).

var setter = Reflector.CreateSetter<object, object>($"Property{i}", typeof(type which contains "Property"+i));
setter(instance, value);

Setter는 유형일 수 Action<object, object>있지만 인스턴스는 런타임에 다를 수 있으므로 setter 목록을 만들 수 있습니다.


-1

불행히도 C #에는 사용자가 요구하는 작업을 정확히 수행 할 수있는 기본 기능이 없습니다.

그러나 내 C # 평가 프로그램에서는 C # 코드를 평가할 수 있습니다. 런타임시 C # 코드 평가를 제공하고 많은 C # 문을 지원합니다. 실제로이 코드는 모든 .NET 프로젝트에서 사용할 수 있지만 C # 구문 사용으로 제한됩니다. 자세한 내용 은 내 웹 사이트 http://csharp-eval.com 을 참조하십시오.



-9

정답은 메모리 사용량을 낮게 유지하기 위해 모든 결과를 캐시해야한다는 것입니다.

예는 다음과 같습니다.

TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
} 

목록에 추가

List<string> results = new List<string>();
for() results.Add(result);

ID를 저장하고 코드에서 사용

도움이 되었기를 바랍니다


5
누군가 평가와 조회를 혼동했습니다. 가능한 모든 프로그램을 알고 있다면 (내가 생각하는적어도 NP-하드) ... 그리고 당신은 모든 가능한 결과를 미리 컴파일하는 supermachine이 ... 그리고 더 sideeffects / 외부 입력 ... 그래,이 아이디어가없는 이론적으로 작품 . 그러나 코드는 하나의 큰 구문 오류입니다
sehe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.