无法通过反射设置字段值

时间:2015-02-08 14:17:32

标签: c# reflection

我开始编写一个非常具体的实用程序方法,它基本上搜索一个给定的对象 - “searchObj”代表字段上的TAttribute,如果找到提供的TAttribute,它会使用提供的“updateValue”参数相应地更新该字段。基本上我最需要的是搜索提供的对象时:

1.如果要搜索的提供对象的字段具有TAttribute,并且该字段是提供的“conditionType”参数的List,则根据更新值自行更新它。

2.如果要搜索的提供对象的字段具有TAttribute,并且该字段是“conditionType”参数提供的 NONMATCHING 类型的列表,则继续搜索List ONLY 表示匹配条件类型的字段,最后如果找到该特定字段,则通过添加或删除元素来将List修改为“updateValue”列表的大小,并仅修改与该字段匹配的字段类型的批评。

因此,对于数字1来说,它很容易实现。我面临的问题是在代码示例中用惊叹号注释掉的。基本上我尝试访问和修改字段的非匹配列表不会设置它的值。它们保持不变,因为它们从未被修改过。我做错了什么?

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

1 个答案:

答案 0 :(得分:1)

这是您用感叹号标记的行及其上方的行:

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

我没有看到做markedList[index].GetType()的重点是返回添加到markedList的新创建对象的类型。你已经知道这将是什么类型,它是genericArgument

所以,让我们将这种简化应用到上面两行代码中的第二行:

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

显然,现在您的代码对markedList[index]无效。你可能想要吗

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

代替?