[英]Searching a array for a specified value in a certain field
我有以下代码并想提高性能(也许使用 LINQ 表达式?)。 无论如何,它应该在数组中搜索第一项,其名称为fieldname
的字段等于传递的 object 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
}
为了获得最佳性能,您需要缓存每种类型的已编译 lambda
尝试:
var containsField = array.Any(item => item.Name == "barbara streisand");
你会在同一个数组上多次调用 ContainsField 吗? 如果是这样,你可以做一个
Dictionary < object, T >
第一次通过,然后使用字典进行所有后续检查。
如果每个数组只调用一次 ContainsField,这并不好。
例如:
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 }
每次切换到不同的数组或不同的字段名时,您需要小心设置 dictionary = null,但您应该明白这一点。
另外,我建议您将 ContainsField 更改为非静态方法,因为它依赖于成员字段在调用之间保留字典。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.