在数组中搜索特定字段中的指定值

时间:2011-04-30 19:22:34

标签: c# .net winforms performance

我有以下代码并希望提高性能(可能使用LINQ表达式?)。 无论如何,它应该搜索数组中第一个项目,其名称为fieldname的字段等于传递的对象obj
例如,假设我们有一个数组Person的数组,其中的字段为NameAge。现在我想知道我的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;
    }

4 个答案:

答案 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更改为非静态方法,因为它依赖于成员字段来在调用之间保留字典。