简体   繁体   中英

Cannot set a field value through reflection

I started to write a very specific utility method, which basically searches a given object - "searchObj" for a TAttribute over a field and if it finds the provided TAttribute it updates that field accordingly with a provided "updateValue" parameter. Basically what i mostly need is when searching the provided object:

1.If the field of the provided object to search has the TAttribute and that field is a List of a provided "conditionType" parameter, to update it self accordingly to the update value.

2.If the field of the provided object to search has the TAttribute and that field is a List of NONMATCHING type provided by the "conditionType" parameter, to continue searching that List ONLY for a field that matches the condition type and finally if it finds that specific field, to modify the List to be the size of the "updateValue" list by adding or removing elements from it and only modify that field which matches the type critteria.

So for number 1 it worked out easy. The problem which i face is commented out with exclamation signs inside the code sample. Basically the non matching list i try to access and modify the field does not set it's values. They stay the same as they were never modified. What am i doing wrong?

/// <summary>
/// Updates all object fields marked with TAtrribute with the provided value. 
/// The attribute field generic argument must meet the condition type in order the values to be correctly updated.
/// </summary>
/// <typeparam name="TAttribute">The attribute to search for</typeparam>
/// <param name="obj">The actual object which will be searched for the attribute</param>
/// <param name="updateValue">The provided value must be a List<conditionType></param>
public static void UpdateAttributeMarkedField<TAttribute>(object searchObj, object updateValue, Type conditionType) where TAttribute : Attribute
{
    Type valueType = updateValue.GetType();
    Type objectType = searchObj.GetType();

    // Get all the public and instance fields from the object
    List<FieldInfo> objectFields = objectType.GetFields(BindingFlags.Instance | BindingFlags.Public).ToList();

    // Search all fields and return the ones marked with the [TAttruibute] attribute as list.
    List<FieldInfo> markedFields = GetAttributeMarkedField<TAttribute>(objectFields);

    for (int i = 0; i < markedFields.Count; i++)
    {
        IList valueList = null;

        if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>))
        {
            valueList = (IList)updateValue;
        }

        // Make sure we only accept lists both for the "obj" and the "value" arguments
        if (markedFields[i].FieldType.IsGenericType && markedFields[i].FieldType.GetGenericTypeDefinition() == typeof(List<>) && valueList != null)
        {
            Type genericArgument = markedFields[i].FieldType.GetGenericArguments()[0];

            // If the marked field is of type List<conditionType> simply just update the values
            if (genericArgument == conditionType)
            {
                markedFields[i].SetValue(searchObj, updateValue);
            }

            // If the marked field is some other type of list, 
            // search for the condition type and if there is one, update it with the provided "value" list.
            else
            {
                FieldInfo[] fields = genericArgument.GetFields();

                bool meetsCondition = false;
                string fieldName = String.Empty;

                // If any marked field meets the condition type get the field name
                for (int j = 0; j < fields.Length; j++)
                    if (fields[j].FieldType == conditionType)
                    {
                        meetsCondition = true;
                        fieldName = fields[j].Name;
                    }

                if (meetsCondition == true)
                {
                    IList markedList = (IList)markedFields[i].GetValue(searchObj);

                    // If the marked list is smaller than the provided value list resize it accordingly by adding the difference.
                    if (markedList.Count < valueList.Count)
                    {
                        int difference = valueList.Count - markedList.Count;

                        for (int j = 0; j < difference; j++)
                        {
                            int index;
                            index = markedList.Add(Activator.CreateInstance(genericArgument));

                            // Update the freshly created field from the condition type to match the freshly created value list
                            // !!!!!!!! DOES NOT SET THE FIELD VALUE !!!!!!!
                            markedList[index].GetType().GetField(fieldName).SetValue(searchObj, valueList[index]);
                        }

                    }

                    // If the marked list is bigger than the provided value list, resize it accordingly by removing the difference.
                    else if (markedList.Count > valueList.Count)
                    {

                    }

                }

            }
        }
        else
        {
            Debug.LogWarning(@"Currently only lists are supported for the ""obj"" and ""value"" arguments. Skipping update for: " + markedFields[i].GetType());
        }

    }

}

public static List<FieldInfo> GetAttributeMarkedField<TAttribute>(List<FieldInfo> searchContext) where TAttribute : Attribute
{
    List<FieldInfo> result = new List<FieldInfo>();
    for (int i = 0; i < searchContext.Count; i++)
        if (Attribute.IsDefined(searchContext[i], typeof(TAttribute)))
        {
            result.Add(searchContext[i]);
        }
    return result;
}

This is the line you mark with exclamation marks, and the line above it:

index = markedList.Add(Activator.CreateInstance(genericArgument));
markedList[index].GetType().GetField(fieldName).SetValue(searchObj, valueList[index]);

I don't see the point of doing markedList[index].GetType() to return the type of the newly-created object added to markedList . You already know what type this is going to be, it's genericArgument .

So, let's apply that simplification to the second of your two lines of code above:

genericArgument.GetField(fieldName).SetValue(searchObj, valueList[index]);

Clearly now your code is doing nothing with markedList[index] . Did you perhaps want

genericArgument.GetField(fieldName).SetValue(markedList[index], valueList[index]);

instead?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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