我有以下代码并希望提高性能(可能使用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;
}
答案 0 :(得分:4)
不是没有涉及某处的循环,没有。当然,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));
}
这是每次迭代使用 less 反射,所以它会比原来快,但是可以通过从{{1创建一个委托来加快速度,快速提取值。代码最终会变得更复杂,但也可能更快。
你肯定有将字段名称指定为字符串而不是(比如说)使用lambda表达式吗?
答案 1 :(得分:2)
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
}
对于最大性能,你想要按类型缓存已编译的lambdas
答案 2 :(得分:1)
尝试:
var containsField = array.Any(item => item.Name == "barbara streisand");
答案 3 :(得分:1)
您是否会在同一个阵列上多次调用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更改为非静态方法,因为它依赖于成员字段来在调用之间保留字典。