在数组中搜索某个字段中的指定值
本文关键字:字段 数组 搜索 | 更新日期: 2023-09-27 17:53:51
我有以下代码,并希望提高性能(可能使用LINQ表达式?)。无论如何,它应该搜索数组中的第一个项,其名称为fieldname
的字段等于传递的对象obj
。
例如,假设我们有一个数组,其中的项类型为Person
,字段为Name
和Age
。现在我想知道369人的数组中是否包含一个名为"barbara streisand"的人。有没有比我现在做的更快的方法,或者甚至没有使用循环的方法?
下面是我的代码:
public static bool ContainsField<T>(this IEnumerable<T> array, string fieldname, object obj)
{
foreach(T val in array)
{
if (val.GetType().GetField(fieldname).GetValue(val).Equals(obj))
return true;
}
return false;
}
不涉及循环某处,不。当然,LINQ使代码更容易阅读。
如果数组中的所有值都是同一类型,则不需要在每次循环迭代时获取该字段:
public static bool ContainsField<T>(this IEnumerable<T> array,
string fieldname,
object obj)
{
Field field = typeof(T).GetField(fieldName);
return array.Any(x => field.GetValue(x).Equals(obj));
}
这是使用更少的反射每次迭代,所以它会比你原来的更快,但你可以使它更快,从Field
创建一个委托,以快速提取值。代码最终会变得复杂得多,但也可能快得多。
您是否一定要将字段名指定为字符串而不是(例如)使用lambda表达式?
LINQ只会在更漂亮的代码中隐藏循环;它不会让它更快。这个循环中真正的慢是反射。如果调用者可以在编译时表示该字段,那将有所帮助(实际上不需要您的方法):
bool containsAny = source.Any(item => item.SomeField == "some value");
但是,如果调用者只知道字段名称,则可以使用元编程执行类似的操作。一个合理的开头可能是:
public static bool ContainsField<T>(this IEnumerable<T> array,
string fieldname, object obj)
{
var param = Expression.Parameter(typeof(T));
var member = Expression.PropertyOrField(param, fieldname);
var body = Expression.Equal(member, Expression.Constant(obj, member.Type));
var lambda = Expression.Lambda<Func<T,bool>>(body, param);
return array.Any(lambda.Compile());
}
使用例子:
static void Main()
{
var data = new[] { new{x = 123}, new{x = 456}, new{x = 789}};
var has = data.ContainsField("x", 789);// true
}
对于最大的性能,您需要缓存每个类型
尝试:
var containsField = array.Any(item => item.Name == "barbara streisand");
你要调用ContainsField多次在同一个数组?如果是这样,你可以做一个
在第一次检查时,然后使用字典进行所有后续检查。如果每个数组只调用一次ContainsField,这是不好的。Dictionary < object, T >
:
每次切换到不同的数组或不同的字段名时,都需要小心地设置dictionary = null,但您应该了解其中的含义。
private Dictionary < object, T > dictionary = null;
public bool ContainsField(IEnumerable < T > array, string fieldname, object obj)
{
if (dictionary == null) // first call, build dictionary
{
dictionary = new Dictionary< object, T >();
foreach (T val in array)
dictionary[val.GetType().GetField(fieldname).GetValue(val)] = val;
}
return dictionary.ContainsKey(obj); // every call use dictionary
}
另外,我建议您将ContainsField更改为非静态方法,因为它依赖于成员字段来在调用之间持久化字典。