开发者

Searching a array for a specified value in a certain field

I have the following code and want to enhance the performance (maybe with LINQ expressions?). Anyways, it should search a array for th first item whose field with the name fieldname equals the passed object obj.

For example, lets say we have a array with items of type Person with the fields Name and Age. Now I want to know if my array of 369 persons contains a person with the name "barbara streisand". Is there a faster way than what I'm doing, or even a way without using a loop?

Heres my code:

 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;
    }


Not without a loop being involved somewhere, no. LINQ makes the code much easier to read, of course.

If all the values in the array will be of the same type, there's no need to get the field on each iteration of the loop though:

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));
}

This is using less reflection per iteration, so it'll be faster than your original, but you could make it faster by creating a delegate from the Field, to extract the value quickly. The code would end up being a lot more complicated, but probably significantly faster too.

Do you definitely have to specify the field name as a string instead of (say) using a lambda expression?


LINQ is only going to hide the loop in prettier code; it won't make it faster. The real slowness in that loop is the reflection. If the caller could express the field at compile-time, that would help (and no need for your method, really):

bool containsAny = source.Any(item => item.SomeField == "some value");

However, if the caller only knows the field name, you could do something similar with meta-programming. A reasonable starter might be:

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());
}

example usage:

static void Main()
{
    var data = new[] { new{x = 123}, new{x = 456}, new{x = 789}};
    var has = data.ContainsField("x", 789);// true
}

for maximum performance, you'd want to cache the compiled lambdas per-type


Try:

var containsField = array.Any(item => item.Name == "barbara streisand");


Are you going to be calling ContainsField many times over the same array? If so, you could make a

Dictionary < object, T >
on the first pass and then use the dictionary for all the subsequent checks. This is no good if you only call ContainsField once per array.

For instance:

        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
        }
You need to be careful to set dictionary = null each time you switch to a different array or a different fieldname, but you should get the idea.

Also, I recommend you change ContainsField to be a non-static method since it depends on a member field to persist the dictionary between calls.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜