希望能够填充对象的任何属性,并在集合中搜索与给定属性匹配的对象。
class Program
{
static List<Marble> marbles = new List<Marble> {
new Marble {Color = "Red", Size = 3},
new Marble {Color = "Green", Size = 4},
new Marble {Color = "Black", Size = 6}
};
static void Main()
{
var search1 = new Marble { Color = "Green" };
var search2 = new Marble { Size = 6 };
var results = SearchMarbles(search1);
}
public static IEnumerable<Marble> SearchMarbles(Marble search)
{
var results = from marble in marbles
//where ???
//Search for marbles with whatever property matches the populated properties of the parameter
//In this example it would return just the 'Green' marble
select marble;
return results;
}
public class Marble
{
public string Color { get; set; }
public int Size { get; set; }
}
}
答案 0 :(得分:5)
不可否认,这很有趣并且花时间。首先,您需要获取search
对象的所有属性,这些属性的值与默认值不同,此方法使用反射是通用的:
var properties = typeof (Marble).GetProperties().Where(p =>
{
var pType = p.PropertyType;
var defaultValue = pType.IsValueType
? Activator.CreateInstance(pType) : null;
var recentValue = p.GetValue(search);
return !recentValue.Equals(defaultValue);
});
然后您可以使用LINQ All
来过滤:
var results = marbles.Where(m =>
properties.All(p =>
typeof (Marble).GetProperty(p.Name)
.GetValue(m) == p.GetValue(search)));
P.s:此代码已经过测试
答案 1 :(得分:2)
您可以像这样使用单独的Filter类:
class Filter
{
public string PropertyName { get; set; }
public object PropertyValue { get; set; }
public bool Matches(Marble m)
{
var T = typeof(Marble);
var prop = T.GetProperty(PropertyName);
var value = prop.GetValue(m);
return value.Equals(PropertyValue);
}
}
您可以按如下方式使用此过滤器:
var filters = new List<Filter>();
filters.Add(new Filter() { PropertyName = "Color", PropertyValue = "Green" });
//this is essentially the content of SearchMarbles()
var result = marbles.Where(m => filters.All(f => f.Matches(m)));
foreach (var r in result)
{
Console.WriteLine(r.Color + ", " + r.Size);
}
您可以使用DependencyProperties来摆脱输入属性名称。
答案 2 :(得分:2)
我将提出通用解决方案,该解决方案将适用于任意数量的属性和任何对象。它也可以在Linq-To-Sql上下文中使用 - 它可以很好地转换为sql。
首先,从定义函数开始,该函数将测试给定值是否被视为非集合,例如:
static public bool IsDefault(object o)
{
return o == null || o.GetType().IsValueType && Activator.CreateInstance(o.GetType()).Equals(o);
}
然后,我们将有一个函数,它构造一个Lambda表达式,并对search
对象中所有set属性的值进行测试:
static public Expression<Func<T, bool>> GetComparison<T>(T search)
{
var param = Expression.Parameter(typeof(T), "t");
var props = from p in typeof(T).GetProperties()
where p.CanRead && !IsDefault(p.GetValue(search, null))
select Expression.Equal(
Expression.Property(param, p.Name),
Expression.Constant(p.GetValue(search, null))
);
var expr = props.Aggregate((a, b) => Expression.AndAlso(a, b));
var lambda = Expression.Lambda<Func<T, bool>>(expr, param);
return lambda;
}
我们可以在任何IQueryable
上使用它:
public static IEnumerable<Marble> SearchMarbles (Marble search)
{
var results = marbles.AsQueryable().Where(GetComparison(search));
return results.AsEnumerable();
}
答案 3 :(得分:1)
如果某个属性具有默认值(即Color == null
和Size == 0
),则假设该属性未填充:
var results = from marble in marbles
where (marble.Color == search.Color || search.Color == null)
&& (marble.Size == search.Size || search.Size == 0)
select marble;
答案 4 :(得分:1)
你可以在Marbles类中重写equals
public override bool Equals(object obj)
{
var other = obj as Marble;
if (null == other) return false;
return other.Color == this.color && other.size == this.size; // (etc for your other porperties
}
然后你可以按
搜索return marbles.Where(m => search == m);
答案 5 :(得分:0)
使用反射,此方法适用于所有类型,无论它们包含多少或哪种类型的属性。
将跳过任何未填写的属性(ref类型为null,值类型为默认值)。如果找到两个不匹配的属性,则返回false。如果所有填写的属性都相等,则返回true。
IsPartialMatch(object m1, object m2)
{
PropertyInfo[] properties = m1.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
object v1 = property.GetValue(m1, null);
object v2 = property.GetValue(m2, null);
object defaultValue = GetDefault(property.PropertyType);
if (v1.Equals(defaultValue) continue;
if (v2.Equals(defaultVAlue) continue;
if (!v1.Equals(v2)) return false;
}
return true;
}
将其应用于您的示例
public static IEnumerable<Marble> SearchMarbles(Marble search)
{
return marbles.Where(m => IsPartialMatch(m, search))
}
GetDefault()是此帖子的方法,Programmatic equivalent of default(Type)
答案 6 :(得分:0)
如果您想避免定位特定属性,可以使用反射。首先定义一个返回类型默认值的函数(对于一个简单的解决方案,请参阅here,对于更精细的解决方案,请参见here。)
然后,您可以在Marble
类上编写一个方法,该方法将Marble
的实例作为过滤器:
public bool MatchesSearch(Marble search) {
var t = typeof(Marble);
return !(
from prp in t.GetProperties()
//get the value from the search instance
let searchValue = prp.GetValue(search, null)
//check if the search value differs from the default
where searchValue != GetDefaultValue(prp.PropertyType) &&
//and if it differs from the current instance
searchValue != prp.GetValue(this, null)
select prp
).Any();
}
然后,SearchMarbles
变为:
public static IEnumerable<Marble> SearchMarbles(Marble search) {
return
from marble in marbles
where marble.MatchesSearch(search)
select marble;
}