c# Iterate array of parent type to invoke non-polymorphic method on derived type

c#

Solution

You could project to the object values first and then work on those in your loop. Your whole code could be boiled down to this (plus your loop):

    /// <summary>
    /// Gets the value of all the fields or properties on an object that are decorated with the specified attribute
    /// </summary>
    private IEnumerable<object> GetValuesFromAttributedMembers<TAttribute>(object obj)
        where TAttribute : Attribute
    {
        var values1 = obj.GetType().GetFields()
                        .Where(fi => fi.GetCustomAttributes(typeof(TAttribute), true).Any())
                        .Select(fi => fi.GetValue(obj));
        var values2 = obj.GetType().GetProperties()
                        .Where(pi => pi.GetCustomAttributes(typeof(TAttribute), true).Any())
                        .Select(pi => pi.GetValue(obj, null));
        return values1.Concat(values2);
    }

Your current code mixes two concerns: finding the values and doing something with them. It would be cleaner to separate those concerns. The above LINQ could be placed in one method that fetches all values from a class that are in fields or properties that match a given attribute and another than is just a loop doing the work on whatever it is passed.

Not as clean but sticking with your original goal you could do this and pass in a delegate appropriate to the type of the MemberInfo you are retrieving:-

    public void DoEachMember<TAttribute, TMembertype>(IEnumerable<TMembertype> members,
                             Func<TMembertype, object> valueGetter)
        where TMembertype : MemberInfo
    {
        foreach (var mi in members)
        {
            if (mi.GetCustomAttributes(typeof(TAttribute), true).Any())
            {
                // Get value of field.
                object value = valueGetter(mi);
                DoAction(value);
            }
        }
    }

Problem

I am using very similar loops to iterate all public fields and properties of any passed object. I determine if the field/property is decorated with a particular custom attribute; if so, an action is performed on the value of the field or property. Two loops are necessary because the method to get a field value is different from the method to get a property value. ``` // Iterate all public fields using reflection foreach (FieldInfo fi in obj.GetType().GetFields()) { // Determine if decorated with MyAttribute. var attribs = fi.GetCustomAttributes(typeof(MyAttribute), true); if (attribs.Length == 1) { // Get value of field. object value = fi.GetValue(obj); DoAction(value); } } // Iterate all public properties using reflection foreach (PropertyInfo pi in obj.GetType().GetProperties()) { // Determine if decorated with MyAttribute. var attribs = pi.GetCustomAttributes(typeof(MyAttribute), true); if (attribs.Length == 1) { // Get value of property. object value = pi.GetValue(obj, null); DoAction(value); } } ``` I would like to place the loop in a single, common method so that I can instead write, more simply: ``` DoEachMember(obj.GetType().GetFields()); DoEachMember(obj.GetType().GetProperties()); ``` This requires `DoEachMember()` to accept the `MemberInfo` type (which is the parent type of both `FieldInfo` and `PropertyInfo`). The problem is there is no `GetValue` method in the `MemberInfo` class. Both `FieldInfo` and `PropertyInfo` use different methods to get the field/property value: ``` public void DoEachMember(MemberInfo mi, object obj) { foreach (MemberInfo mi in obj.GetType().GetProperties()) { object value mi.GetValue(obj); // NO SUCH METHOD! } } ``` Thus, I declare a delegate to utilize inside the loop which takes a `MemberInfo` and returns the value of that member as an object: ``` // Delegate to get value from field or property. delegate object GetValue(MemberInfo mi, object obj); ``` The Question How can I detect the type of the objects in the `members[]` array, in order to define the delegate used inside the loop? Currently, I am using the first element of the array, `members[0]`. Is this a good design? ``` public void DoEachMember(MemberInfo[] members, object obj) { // Protect against empty array. if (members.Length == 0) return; GetValue getValue; // define delegate // First element is FieldInfo if (members[0] as FieldInfo != null) getValue = (mi, obj) => ((FieldInfo)mi).GetValue(obj); // First element is PropertyInfo else if (members[0] as PropertyInfo != null) getValue = (mi, obj) => ((PropertyInfo)mi).GetValue(obj, null); // Anything else is unacceptable else throw new ArgumentException("Must be field or property."); foreach (MemberInfo mi in members) { // Determine if decorated with MyAttribute. var attribs = mi.GetCustomAttributes(typeof(MyAttribute), true); if (attribs.Length == 1) { object value = getValue(mi, obj); DoStuff(value); } } } ``` Alternatively, I could detect the type upon each iteration, but there should be no reason individual array members will ever differ: ``` foreach (MemberInfo mi in members) { // ... object value; if ((var fi = mi as FieldInfo) != null) value = fi.GetValue(obj); else if ((var pi = mi as PropertyInfo) != null) value = pi.GetValue(obj, null); else throw new ArgumentException("Must be field or property."); DoStuff(value); } ```

Original source