繁体   English   中英

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

[英]Searching a array for a specified value in a certain field

我有以下代码并想提高性能(也许使用 LINQ 表达式?)。 无论如何,它应该在数组中搜索第一项,其名称为fieldname的字段等于传递的 object 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
}

为了获得最佳性能,您需要缓存每种类型的已编译 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM