[c#] C # 코드를 동적으로 평가하려면 어떻게해야합니까?
eval("something()");
JavaScript에서 코드를 동적으로 실행할 수 있습니다 . C #에서 동일한 작업을 수행 할 수있는 방법이 있습니까?
내가하려는 작업의 예는 다음과 같습니다. 정수 변수 (예 i
:)가 있고 “Property1”, “Property2”, “Property3″등의 이름으로 여러 속성이 있습니다. 이제 몇 가지 작업을 수행하고 싶습니다. 의 값에 따라 “속성 i “속성에 i
.
이것은 Javascript로 정말 간단합니다. C #으로이 작업을 수행 할 수있는 방법이 있습니까?
답변
불행히도 C #은 이와 같은 동적 언어가 아닙니다.
그러나 할 수있는 일은 클래스와 모든 것이 포함 된 C # 소스 코드 파일을 만들고 C # 용 CodeDom 공급자를 통해 실행하고 어셈블리로 컴파일 한 다음 실행하는 것입니다.
MSDN의이 포럼 게시물에는 페이지 아래에 몇 가지 예제 코드가 포함 된 답변이 포함되어 있습니다
. 문자열에서 익명 메서드를 만드시겠습니까?
나는 이것이 매우 좋은 해결책이라고 거의 말하지 않지만 어쨌든 가능합니다.
그 문자열에서 어떤 종류의 코드를 기대합니까? 예를 들어 수학 표현식과 같이 유효한 코드의 사소한 하위 집합 인 경우 다른 대안이있을 수 있습니다.
편집 : 글쎄요, 먼저 질문을 철저히 읽도록 가르쳐줍니다. 예, 반성하면 여기서 도움을 줄 수 있습니다.
문자열을; 먼저 개별 속성을 가져 오려면 다음 코드를 사용하여 클래스의 특정 속성에 대한 PropertyInfo 개체를 가져온 다음 해당 개체를 사용하여 특정 개체를 조작 할 수 있습니다.
String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);
답변
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();");
답변
별로. 리플렉션을 사용하여 원하는 것을 얻을 수 있지만 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);
}
답변
이것은 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;
}
답변
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를 기반으로 합니다.
답변
그 모든 것이 확실히 작동 할 것입니다. 개인적으로 그 특정 문제에 대해서는 아마도 조금 다른 접근 방식을 취할 것입니다. 아마도 다음과 같습니다.
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
}
}
이와 같은 패턴을 사용할 때 데이터가 값이 아닌 참조로 저장된다는 점에주의해야합니다. 즉, 프리미티브로이 작업을 수행하지 마십시오. 당신은 그들의 큰 부풀린 클래스 대응 물을 사용해야합니다.
나는 그것이 정확히 질문이 아니라는 것을 깨달았지만 그 질문에 대한 대답은 꽤 잘되어 있었고 대안적인 접근법이 도움이 될 것이라고 생각했습니다.