如果你是一个C#开发人员,学会用Expression,会对编写一些功能带来一些好处,例如我们在开发一个分页功能时,会拥有很多的查询条件,对于新手来说这些查询条件的代码他们可能会通过拼接 sql的where条件方式来处理,当你学会用表达式,这将是非常简单的一个功能。
这个贴子中的示例代码,是来源于我在基于 asp.net 实现服务器端对提交的表单数据自动进行合法性验证时提取出的代码,因为其中有一个功能就是根据表单控件添加的自定义属性进行”>”, “<“, “=”, “>=”, “<=”, “!=”运算,验证该控件的值是否合法,这是使用Expression的最佳案例,当然如果是ASP.NET Core我们也是可以这样使用Expression来实现非常有用的功能。
Expression调用字符串Contains
最近项目中遇到一个需求,需要动态使用表达式Expression调用字符串的Contains方法,记录C#构造Expression调用字符串Contains 犯的1个错误,在初始写的代码中,因为参数写的有点问题,浪费了一些时间寻找原因,不过最终还是找到了,因此记录一下犯过的这个错误。
下面是我原始的代码。
class Program { static void Main(string[] args) { var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var par1 = Expression.Parameter(typeof(string), "x"); var par2 = Expression.Parameter(typeof(string), "y"); var body = Expression.Call(par1, method, par2); var r = Expression.Lambda<Func<string, string, bool>>(body, par2).Compile()("jhrs.com is my blog or my blog address is https://jhrs.com", "jhrs.com"); Console.WriteLine(r); } }
将程序运行起来会得到如下的错误。
错误内容是:
Unhandled exception. System.ArgumentException: Incorrect number of parameters supplied for lambda declaration at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type delegateType, Expression& body, ReadOnlyCollection`1 parameters, String paramName) at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, String name, Boolean tailCall, IEnumerable`1 parameters) at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, Boolean tailCall, IEnumerable`1 parameters) at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, ParameterExpression[] parameters) at ConsoleApp1.Program.Main(String[] args) in F:\jhrs.crawler\ConsoleApp1\Program.cs:line 17
出错的原因是什么
当我看到上图所示的错误时,浪费了一些时间来寻找出错的原因,最终定位到的原因是传参错误,当我把正确的代码写出来后,就可以明显的发现和原来的代码的区别。
下面向你展示的是正确的写法:
class Program { static void Main(string[] args) { var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var par1 = Expression.Parameter(typeof(string), "x"); var par2 = Expression.Parameter(typeof(string), "y"); var body = Expression.Call(par1, method, par2); var r = Expression.Lambda<Func<string, string, bool>>(body, new ParameterExpression[] { par1, par2 }).Compile()("jhrs.com is my blog or my blog address is https://jhrs.com", "jhrs.com"); Console.WriteLine(r); } }
好了,我们来对比一下看有什么区别:
class Program { static void Main(string[] args) { //Wrong for the first time, an error will be reported here var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var par1 = Expression.Parameter(typeof(string), "x"); var par2 = Expression.Parameter(typeof(string), "y"); var body = Expression.Call(par1, method, par2); var r = Expression.Lambda<Func<string, string, bool>>(body, par2).Compile()("jhrs.com is my blog or my blog address is https://jhrs.com", "jhrs.com"); Console.WriteLine(r); //Here is the right code //var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); //var par1 = Expression.Parameter(typeof(string), "x"); //var par2 = Expression.Parameter(typeof(string), "y"); //var body = Expression.Call(par1, method, par2 ); //var r = Expression.Lambda<Func<string, string, bool>>(body, new ParameterExpression[] {par1, par2 }).Compile()("jhrs.com is my blog or my blog address is https://jhrs.com", "jhrs.com"); //Console.WriteLine(r); } }
出错的原因正如上图我使用红色标记的地方那样,是因为给了错误的参数。
结论
当我解决了这个问题后,再来复盘这个问题,发现自己犯了一个低级的错误,事实上在编写代码的过程中,如果你是在Visual Studio 2019编写的这段程序,只需要使用快捷键CTRL+SHIFT+SPACE就可以得到如下图的提示。
希望你编码愉快。