Using an object as an array accessor through Reflection

Fun bit of code here using System.Reflection!

I have a class that contains an array of objects.  I wanted a convenient way to be able to access elements by the value of a property within the object, for example, if I have an array of type Employee and I want to find the one in the array where the LastName Property is Smith, it would be nice to be able to call MyArray[“EmployeeID”, “12345”] to get the item.  Basically, adding some Dictionary-type lookup options to an indexed array is what I wanted.

My solution is:

public class ccnGenericCollection<T>
{
    #region Fields
    private T[] _Items;
    #endregion

    #region Properties
    public T[] Items
    {
        get { return _Items; }
        set { _Items = value; }
    }

    public T this[int index]
    {
        get { return _Items[index]; }
        set { _Items[index] = value; }
    }

    public T this[string fieldName, object searchValue]
    {
        get
        {
            // Make sure the items and key field name are initialized
            if (null != _Items)
            {
                // Get the member 
                MemberInfo[] MyMembers = typeof(T).GetMember(fieldName);
                if ((null != MyMembers) && (MyMembers.GetLength(0) > 0))
                {
                    MemberInfo MyMember = MyMembers[0];
                    // Loop through the items
                    for (int i = 0; i < _Items.GetLength(0); i++)
                    {
                        object MyKeyValue;
                        // Get the field or property value
                        switch (MyMember.MemberType)
                        {
                            case MemberTypes.Field:
                                MyKeyValue = ((FieldInfo)MyMember).GetValue(this&#91;i&#93;);
                                break;
                            case MemberTypes.Property:
                                MyKeyValue = ((PropertyInfo)MyMember).GetValue(this&#91;i&#93;, null);
                                break;
                            default:
                                throw new Exception(fieldName + " must be a field or a property");
                        }
                        if (MyKeyValue.Equals(searchValue))
                        {
                             // Found our item
                             return _Items&#91;i&#93;;
                        }
                    }
                }
            }
            // Not found, return a default item
            return default(T);
        }
        set
        {
            if (null != _Items)
            {
                // Get the member
                MemberInfo&#91;&#93; MyMembers = typeof(T).GetMember(fieldName);
                if ((null != MyMembers) && (MyMembers.GetLength(0) > 0))
                {
                    MemberInfo MyMember = MyMembers[0];
                    // Loop through the items
                    for (int i = 0; i < _Items.GetLength(0); i++)
                    {
                        switch (MyMember.MemberType)
                        {
                            case MemberTypes.Field:
                                if (((FieldInfo)MyMember).GetValue(this&#91;i&#93;).ToString() == searchValue)
                                {
                                    // Set the item
                                    _Items&#91;i&#93; = value;
                                }
                                break;
                            case MemberTypes.Property:
                                if (((PropertyInfo)MyMember).GetValue(this&#91;i&#93;, null).ToString() == searchValue)
                                {
                                    // Set the item
                                    _Items&#91;i&#93; = value;
                                }
                                break;
                        }
                    }
                }
            }
            // No item found, throw a null reference exception
            throw new NullReferenceException();
        }
    }
    #endregion

    #region Operators
    public static implicit operator T&#91;&#93;(ccnGenericCollection<T> input)
    {
        // Automatically convert from an array of the underlying class to the generic collection
        T[] Output = (T[])input.Items.Clone();
        return Output;
    }

    public static implicit operator ccnGenericCollection<T>(T[] input)
    {
        // Automatically convert from the generic collection to an array of the underlying class
        ccnGenericCollection<T> Output = new ccnGenericCollection<T>();
        Output.Items = (T[])input.Clone();
        return Output;
    }

    public static implicit operator List<T>(ccnGenericCollection<T> input)
    {
        // Automatically convert from an array of the underlying class to the generic collection
        List<T> Output = new List<T>();
        if (null != input.Items)
        {
            Output.AddRange(input.Items);
        }
        return Output;
    }

    public static implicit operator ccnGenericCollection<T>(List<T> input)
    {
        // Automatically convert from the generic collection to an array of the underlying class
        ccnGenericCollection<T> Output = new ccnGenericCollection<T>();
        Output.Items = (T[])input.ToArray().Clone();
        return Output;
    }
    #endregion

    #region Methods
    public int Add(T newItem)
    {
        // Get the list of the current items
        List<T> MyList = (List<T>)this;
        // Add the new item
        MyList.Add(newItem);
        // Set the items to the array of the list
        _Items = MyList.ToArray();
        // Return the highest index of the items
        return _Items.GetUpperBound(0);
    }
    #endregion
}

This solution allows the field name for the search to be either a field or a property.

Note that it’s prudent to make sure that the field used to access the class is unique; it’ll return the first match for get and update all matches for set.

I got a surprise the first time I ran it when I never got any matches back by using MyKeyField == searchValue as the condition; I had to use MyKeyField.Equals(searchValue) to use the object equality. This also means that it’s easy to define what equality is for a given class by implementing the == operator for the class.

The implicit operators come in handy as well; basically any class that implements this class can be converted to/from arrays and lists.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s