new CodeMethodReferenceExpression(null, "Factorial"),
new[]
{
new CodeBinaryOperatorExpression(
new CodeVariableReferenceExpression("number"),
CodeBinaryOperatorType.Subtract,
new CodePrimitiveExpression(1))
})))
});
method.Statements.Add(condition);
כעקרון זה די לא קשור לEval, אבל זה נותן תמונה כללית טובה על מה זה CodeDom: למשל כדי לעשות return מפונקציה, אנחנו יוצרים CodeMethodReturnStatement וכדי לקרוא למתודה, אנחנו יוצריםCodeMethodInvokeExpression וכו’.
אפשר לעשות עם CodeDom הרבה דברים טובים: הFeature העיקרי זה לג’נרט קוד משלנו שיעשה ניסים ונפלאות (ולבחור שפה – C# או VB!).
אבל קיימת עוד אופציה – אנחנו יכולים לקמפל את הקוד בזמן ריצה ולקבל ממנו Assembly – זה מתבצע ע"י CodeDom ע"י קריאה לcsc.exe – זה הC# Compiler.
בנוסף, אפשר לקמפל בעזרת CodeDom גם string חופשי – ופה נכנס לסיפור הEval:
אנחנו יכולים להשתמש בסיפור הזה כדי להריץ Eval משלנו, זה יראה בערך ככה:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
publicstatic T Evaluate<T>(string expression)
{
string code =
string.Format(
@"using System;
using System.Linq;
public class Evaluation
{{
public static {0} Evaluate()
{{
{0} value = {1};
return value;
}}
}}", FormatType(typeof(T)),
expression);
CSharpCodeProvider provider = new CSharpCodeProvider
(new Dictionary<string, string>
{
{ "CompilerVersion", "v4.0" }
});
CompilerParameters compilerParameters = new CompilerParameters();
מה שעשינו פה קצת מגעיל: יצרנו מחלקה ממחרוזת שיש בה פונקציה סטטית שאנחנו רוצים, קימפלנו אותה, הרצנו אותה, והחזרנו את ערך ההחזר.
מה שקורה במתודה זה שאנחנו מוסיפים את כל הReferenceים שצריך, דואגים להתקמפל לFramework 4.0, מקמפלים את המתודה, וקוראים לפונקציה בReflection
הפונקציה FormatType שלא צורפה כאן היא פונקציה שמקבלת Type וכותבת אותו בצורה C#ית יפה (עם למשל System.Func<System.Double, System.Double> במקום הFullName שמחזיר
ככה אפשר לעשות דברים ממש מגניבים, למשל ליצור מתודות ואובייקטים מקלט של המשתמש 😃
יש פה כמה חסרונות:
אחד החסרונות הוא שהקימפול מתבצע ע"י הcsc.exe, כך שאנחנו יוצאים לexe חיצוני שהוא unmanaged בדרך. היה יותר נחמד אם הכל היה מתבצע בצורה Managed.
בנוסף, חסרון נוסף שיש כאן, הוא שכל פעם שאנחנו קוראים למתודה הזאת, נטען Assembly נוסף לAppDomain. הבעיה בטעינת Assemblyים לAppDomain, הוא שאי אפשר לעשות להם unload, וככה אפשר לפוצץ את הAppDomain בAssemblyים.