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

本文关键字:字段 数组 搜索 | 更新日期: 2023-09-27 17:53:51

我有以下代码,并希望提高性能(可能使用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;
    }

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

不涉及循环某处,不。当然,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
}

对于最大的性能,您需要缓存每个类型

编译的lambdas。

尝试:

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