我正在构建Linq
扩展方法。
很快,我构建了一个扩展方法,以便创建MemberExpression
看起来像:
public static Expression Field<T>(this object entity, string field)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Property(parameterExpression, propertyInfo);
}
所以,我能够做到这一点:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").EndsWith(string.Empty)<<<<<<<< Compilation error.
);
由于MemberExpression
没有EndsWith
方法,我无法将此MemberExpression
扩展为String
属性访问权限,如:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Matter.EndsWith(string.Empty)
);
有没有办法做到这一点。
正如你能够弄清楚我试图让事情变得更复杂一点,不过,这个例子是为了解释这种情况。
我希望它很清楚。
范围
因此,我的UI能够处理集合,数据库或从我们的服务器获取数据。
我想提供像AnyField()
这样的util扩展方法。
AnyField()
生成一个表达式树,每个Linq提供程序都可以翻译它(本文的第一个答案)。Anyfield()
的默认实现,然后使用每个Linq提供程序扩展机制来处理它。或者,如果您正在构建Linq提供程序,请在实现时支持它。答案 0 :(得分:2)
好的,所以当你构建ExpressionTrees时,你会惹恼C#为你提供的语法糖
Where
期望Expression<Func<TObjectType, TReturnType>>
或编译的lambda; Func<TObjectType, TReturnType>
。
您的方法Field
目前只返回无类型Expression
。这意味着您的查询实际返回Expression<Func<TObjectType, Expression>>
。那是不对的!它应该返回Expression<Func<TObjectType, string>>
!但是我们怎么做呢?这意味着我们的方法必须返回string
,但我们想要构建一个表达式树。
为了让它像你期望的那样工作,它比你想象的要困难得多,但那只是因为我们被语法糖所迷惑了。
我们真正需要做的是编写接受lambda方法的方法,然后返回 lambda方法,每个方法都重写一下。
那么......那是什么样的?
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
请注意,它与您编写的内容几乎完全相同,但我们用Lambda<Func<TElementType, TReturnType>>
包装它。签名也有点不同。
我们希望对lambda表达式进行操作,而不是对对象进行操作。我们还返回一个lambda表达式。
那么我们如何使用它?
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
);
大!现在我们将Expression<Func<Test, string>>
传递给Where
,而不是Expression<Func<Test, MemberExpression>>
。取得进步。
但那不会编译,这是正确的。我们正在返回一个字符串,但我们正在使用一个过滤方法,这需要一个bool。
现在让我们写一下EndsWith:
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
使用它:
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
.EndsWith("A")
);
现在正在编译!表达式树看起来像这样:
UserQuery+Test[].Where(e => e.Matter.EndsWith("A"))
这不是太漂亮,但Field
采取了多余的lambda。让我们添加一个帮助方法,让它看起来更漂亮:
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
全部放在一起:
void Main()
{
var objects = new[] { new Test { Matter = "A" } }.AsQueryable();
var classes = objects.Where(
ExpressionExtensions.Query<Test>(q => q)
.Field("Matter")
.EndsWith("A")
);
classes.Expression.Dump();
}
public class Test
{
public string Matter { get; set;}
}
public static class ExpressionExtensions
{
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
}
答案 1 :(得分:0)
我不知道你是否弄乱了代码以显示一段代码或是否有意,但你有一个通用的扩展,想要A T但你不能使用它
无论如何,如果您想要的是一个返回属性值的方法,为什么不做一个返回T的静态异常?
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
这是一个小提琴,我已经完成了向您展示其用法,非常简单 https://dotnetfiddle.net/PoSfli
发布代码也是为了小提琴丢失:
using System;
using System.Reflection;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
YourClass c = new YourClass() {
PropA = 1,
PropB = 2,
PropC = "ciao"
};
var propBValue = c.Field<int>("PropB");
Console.WriteLine("PropB value: {0}", propBValue);
var propCValue = c.Field<string>("PropC");
Console.WriteLine("PropC value: {0}", propCValue);
}
}
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
public class YourClass {
public int PropA { get; set; }
public int PropB { get; set; }
public string PropC { get; set; }
}
nota你可以改进很多,使用类型扩展和属性表达式作为参数而不是字符串
答案 2 :(得分:0)
这样的事情怎么样?实际上,你的通用方法现在还没有用。
public static bool Evaluate<TField>(this object entity, string fieldName, Predicate<TField> condition)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
var value = (TField)propertyInfo.GetValue(entity); //read the value and cast it to designated type, will raise invalid cast exception, if wrong
return condition.Invoke(value); //invoke the predicate to check the condition
}
然后用法。
.Where(item => item.Evaluate<string>("Matter", prop => prop.EndsWith(string.Empty))
答案 3 :(得分:0)
如果你想使用属性名称,你也可以做一些非常简单的事情:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").ToString().EndsWith(string.Empty)
或者,如果您按属性类型进行过滤:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Type.ToString().EndsWith(string.Empty)
答案 4 :(得分:0)
您可以添加一个新的扩展方法,返回所需的类型。
main.html
在您的陈述中,您只需添加$(function() {
$('#menu a').click(function() {
$.get('option.html', function(data) {
$('#form').replaceWith(data);
});
});
});
body {
padding: 0;
margin: 0;
overflow-y: scroll;
font-family: Arial;
font-size: 15px;
}
#container {
background-color: #707070;
}
#menu {
width: 1250px;
margin: 0 auto;
text-align: left;
}
#menu ul {
list-style-type: none;
/*Remove bullets*/
padding: 0;
margin: 0;
position: relative;
}
#menu ul li {
display: inline-block;
}
#menu ul li:hover {
text-decoration: none;
color: black;
}
#menu ul li a,
visited {
color: #CCC;
/*Color of text*/
display: block;
/*Takes up the full width available*/
padding: 15px;
text-decoration: none;
}
#menu ul li a:hover {
color: #CCC;
text-decoration: none;
}
#menu ul li:hover ul {
display: block;
}
#menu ul ul li {
display: block;
}
#menu ul ul {
display: none;
position: absolute;
background-color: #707070;
min-width: 140px;
/*Width when hover*/
}
#menu ul ul li a:hover
/*Color of text when hover*/
{
color: #099;
}