返回首页

{A}{S0}目录{A6}{A7}{A8}简介
将用户输入转换为强类型的值是几乎任何应用程序的一部分。即使服务器,没有任何用户界面,往往要分析文本/ XML配置文件和解释人类更多可用的东西写进文本值。
例如,想象一个简单的应用程序,从互联网上下载文件,并将它们保存到本地磁盘。保存的文件的文件属性读取配置文件或命令行,然后转换为FileAttributes枚举。 "读只normalquot类型的属性管理员可能有quot; 0x081quot,或"129quot,或"只读| Normalquot,或quot;正常Readonlyquot;所有这些是毫不含糊的的意义,但解析是令人惊讶的困难和冗长。简单的东西是需要的,理想的那样简单:

FileInfo f=new FileInfo("x.txt");

f.Attributes=Utils.To<FileAttributes>("ReadOnly | 0x80"); 

另一个相关的,共同的任务分析和评估表达式。这些来有用的,在许多情况下,COM互操作隐藏的System.Reflection冗长解释复杂的配置文件,通过从客户端到服务器的排序表达。
有很多的表现评价到。NET框架的部分解决方案。从使用CSharpCodeProvider到LINQ,ASP.NET DataTable的计算列使用反射和Reflection.Emit。当然它的也可能到添加的IronPython,等虽然解决这个问题,这些解决方案中添加几百毫秒的执行时间(例如调用C#编译器),或者是不容易,以扩展/修改,或者是复杂的,或不开源,或吃大量的内存,或使用的语法不直观的C#程序员,或添加不必要的依赖关系。事情变得更容易一点。NET 4.0和动态,但{A9}需要一个解决方案,只有工作。NET 2.0中。
用于计算表达式编写代码,甚至编程语言肯定是有很多乐趣,数以千计的书籍和例子存在,并有许多{A10},可用于任何语言生成的词法分析器和解析器。
,但我想要的东西简单,更小和集中评估的一部分。一个完整的表达式求值引擎,将能够解析和评估quot;通常lookingquot; C#的表达式(用更宽松的语法)和工作正常NET库。得多比完整的C#intepreter,绝对不是一个完整的编程语言简单的东西。结构紧凑,易于组装一堆的源代码文件或直接嵌入到任何项目,不会有任何依赖关系,除了C#编译器的东西是。
所以,与做分析器quot; properlyquot;通过编译成数千生成的C#代码行的语法,被遗弃。它的更多的乐趣,反正从头写:)。 {A11},其核心是基于一些C#特定的东西,如类型转换,数组的初始化,短circuting等。而凌乱的部分是处理与评价,这是特别难,因为我想完全分开解析和类型绑定。这增加了灵活性,但是否xyz是静态属性的类型XY Z,或一个领域对象x Ÿ的财产ž的,或上述的一些其他组合,这一决定是推迟到运行时分析器不能确定。
最后,有一个表达评估引擎得到放宽的类型转换和EVAL完成,使用简单:{C}
可用于COM互操作,隐藏反映冗长,而不需要。NET 4.0或类型库导入。例如,下面的打印有关当前的本地互联网信息服务器的SSL证书的信息:
var context=new BasicEvaluationContext();

context.SetVariable("iis", Utils.CreateComObject("IIS.CertObj"));

var cert=context.Eval<string>(@"

    $iis.set_ServerName('localhost'); // set ServerName property to local IIS server

    $iis.set_InstanceName('w3svc/1'); // set InstanceName property to the first website 

    Encoding.Unicode.GetString($iis.GetCertInfo());

");

Console.WriteLine("Current certificate is:\n{0}",cert);
表达式语法
编写一个完整的符合标准的C#解释器或编译器是不是一个计划(何必如果一个真正的编译器csc.exe的服务到家吗?),所以一些功能被削减和保持控制之下的复杂性的简化。不支持的功能转让,左值,后缀递增,等编写= BC是不可能的。无论是B也不N = B] =- 8。仍然可以使用x.set_PropertyName(值)的语法设置属性。重写运营商。对不起,DateTime和TimeSpan的不能相加日期跨度。 date.Add(SPAN)语法,而不是要使用。C#3初始化,如新的X {A = 3}匿名类,LINQ中,代表,事件等。转义字符。 "你好\ N \ rquot;是9个字符组成的字符串。模板(它可以调用模板对象的方法,但不能创建)。多维数组,像新的int [4,2]。交错数组,像[B] [C],都很好。支持的功能通常,-,*,/, | |,放大器;放大器;,因为,类型转换等有条件的经营者,X Y():Z()正确短路。空合并运算符??B. 逻辑运算符| |和放大器;放大器,其中短路以及。评论(/ / / * * /)与数组的初始化,。例如INT [] {1,2,3}新抛出的运营商,如新的FileInfo("C:\ File.exequot;)命名空间
也有额外的功能,将在更详细的{A12}解释。使用代码
EVAL库中最重要的类是:ParsingReader,解析文本输入一些有用的方法,从TextReader的派生:阅读引号的字符串和数字流(所有0x3233l,0.21e31和211.2米语法的各种),跳过空白,等也保持着历史,所以如果ParsingException抛出,它可以显示发生问题的确切位置。utils的类包含与Tolt一堆静态实用方法; TGT;(对象)类型之间的轻松转换特别有用。Parser类输入文字转换成一个表达式树。IOperation接口代表一个表达式树的单节点。IEvaluationContext接口是用于表达式树评价的上下文。BasicEvaluationContext IEvaluationContext接口的简单实现。
一般来说,代码应该是如下,与分析器的创建,表达解析,然后在栈评估:
TextReader input = ...; // This is the source of the expression



using (ParserReader reader=new ParserReader(input))

{

    // Parse text to expressionTree

    Parser parser=new Parser();

    IOperation expressionTree=parser.Parse(reader);



    // Evaluate the expression tree in certain context on stack

    IEvaluationContext context=new SomeClassImplementingIEvaluationContext();

    Stack<object> stack=new Stack<object>();

    expressionTree.Eval(context,stack);



    // On top of the stack there is a return value

    object resultValue=stack.Pop();

    Console.WriteLine("Result: "+resultValue);

}

如果只是简单的东西是需要,BasicEvaluationContext可以使其尽量简短:
new BasicEvaluationContext().Eval("Console.WriteLine('Hello, world')");
IEvaluationContext
IEvaluationContext提供在表达式中引用的对象的值,并给出了访问类型系统和外部方法。这个接口有6个方法:
/// Evaluation context, providing interface to the external resources

public interface IEvaluationContext

{

    /// Get external variable (variable specified as $xxx. For example, $x+$y )

    bool    TryGetValue(string name, out object value);



    /// Find external type. Returns null if type not found



    Type    FindType(string name);



    /// Call external method

    object  CallExternal(string name, object[] parameters);



    /// Try to get external object. This is different from variable 

    /// and not prepended by $. For example, c.WriteLine('hello')

    bool    TryGetExternal(string name, out object value);



    /// Get list of no-name objects or type to try methods that start with .

    IEnumerable <TypeObjectPair> GetNonameObjects();



    /// Returns true if private members may be accessed

    bool    AccessPrivate { get; }

}  

简单,并准备使用IEvaluationContext实施BasicEvaluationContext包括,看到它的{A13}更多细节。
首先,解释引擎有两个不同的概念:外部对象和值:外部对象的访问通过正常的C#一样,不能包含空格和特殊字符的标识符。例如,AB评价将与quot调用TryGetExternal的两倍; aquot;和"bquot; BasicEvaluationContext增加了空,真与假其默认情况下的对象名单。变量是类似的对象,但使用$前缀和可选的{}(有用的空间和非ASCII字符的名称)的Perl / PHP的语法,表达式中引用。在美元价格的情况下* $ {项目数}引擎会调用TryGetValue quot; pricequot;和quot; itemsquot数量;。变量名也可以是一个空字符串,例如:$ toString()的长度评价长度$ {}的toString()将调用TryGetValue("")。。两倍。
类型的名称。NET类型的分辨率高达FindType方法的实施。这可以作为一个基本的安全功能,所以表达式类型清单可能不使用代码访问安全性限制。
显然,表达可以调用任何对象的访问属性的方法,使用通常的obj.method()语法。未指定对象时,如在COS(X)/ SIN(X),CallExternal接口的方法是寻找并调用适当的方法或抛出异常,如果这是不可能的:
class EvalContext : BasicEvaluationContext

{

    public override object CallExternal(string name, object[] parameters)

    {

        switch (name.ToUpperInvariant())

        {

            case "SIN":

                if (parameters.Length==1)

                    return Math.Sin(Utils.To<double>(parameters[0]));

                break;

            case "COS":

                if (parameters.Length==1)

                    return Math.Cos(Utils.To<double>(parameters[0]));

                break;                  

        }

        return base.CallExternal(name,parameters);

    }

}

void Main()

{

    var context=new EvalContext();

    context.Objects["pi"]=Math.PI;

    Console.WriteLine(context.Eval("cos(pi)/sin(pi/4)"));   



}

// -1.4142135623731    

表达式也可自动调用某些对象或通过特殊点语法类的方法。例如,三角函数定义在类MyTrig中。现在,如果实施的GetNonameObjects返回MyTrig对象的一个​​实例,表达式可能被写入为quot; COS(X)/ SIN(X)quot;,它会调用该MyTrig对象相应的方法。此语法差异可能是使用内置和用户定义的函数的名称,以避免冲突。
class MyTrig 

{

    private bool _useDegrees=false;

    public MyTrig(bool useDegrees)  { _useDegrees=useDegrees; }

    public double Sin(double x)     { return Math.Sin(_useDegrees?(x/180)*Pi:x); }

    public double Cos(double x)     { return Math.Cos(_useDegrees?(x/180)*Pi:x); }

    public double Pi                { get { return Math.PI; }}

}

void Main()

{

    var context=new BasicEvaluationContext();

    context.AddNonameObject(new MyTrig(true));

    Console.WriteLine("Cos of 45 degrees is "+context.Eval(".cos(45)"));   

}

// Cos of 45 degrees is 0.707106781186548   

最后,如果AccessPrivate返回true,表达式将被允许访问非公共方法和属性的对象。一些额外的说明大小写敏感性
变量和对象的名称可能是大小写敏感或不取决于IEvaluationContext实施。与C#不同,方法和属性名称不区分大小写。类型转换
类型转换在C#中轻松得多。例如,(字符串)(布尔)0x21是有效的(计算字符串真实),所以(FileAttributes)0 X32 |正常"。即使(CHAR)"aquot;工程,并返回字符串的第一个字符。逗号和分号运算符
逗号和分号C运算符逗号的含义相同,返回列表中的最后价值。 (),B()C()调​​用3个函数,并返回结果的C()。多表达式
除了单一的表达式,有也多表达的概念,语法,比如$ {A | B | C | = 5}。如果它被定义,它会返回变量a的值。如果不是,它将尝试获得B值如果失败了,价值C,最后一个整数5。这是一个便捷的方式提供默认值给变量。字符类型和字符串
字符串可能被引用使用quot;(双引号),'(单引号)和`(反引号)互换,结果类型始终是字符串,不是char。如果char类型的需要,必须进行显式转换一样(CHAR)``或新的字符串((char)的8,21)。
另外,还有一些没有转义字符的字符串。要创建\ X08字符的字符串,例如,连接应该用来代替:"AAAquot;(CHAR)0x8(CHAR)0x8(char)的0x8"BBB"。阵列
阵列可以创建没有新的,只使用{}块。例如,{1,2,3}的计算结果为3个整数的数组,{1,2.4,3} 3双打的数​​组,和{1,"2.4",3} 3对象的数组。为运营商的替代语法
LT,GT,放大器;字符不方便,难以阅读嵌入到XML表达时。下面可以用来代替

回答

评论会员:阿列克谢Shamov 时间:2012/01/26
rapid2k2
评论会员:游客 时间:2012/01/26
优秀是远远不够的!这消除了需要使用更多的功能CompileAssemblyFromSource东西就是这么慢,
!KunalChowdhury
评论会员:游客 时间:2012/01/26
看起来不错和信息。搞好。坚持做下去。imgsrc=http://www.orcode.com/img/ico/thumbs_up.gif我没有任何其他选择超过5。{S2}三江源CodeProject上张贴。关于-Kunal乔杜里|软件开发|金奈|印度|{A16}
Sandeep Mewara
评论会员:游客 时间:2012/01/26
,有人不喜欢我们的意见和downvoted我们。反驳。imgsrc=http://www.orcode.com/img/ico/thumbs_up.gifSandeepMewara{A17}