C# Разработка .Net3.5 с использованием отражения для получения/установки значений вложенных свойств и/или вложенных полей

Я разрабатываю приложение, которое работает с классами блоков данных, унаследованными от базового класса, и я пытаюсь использовать Reflection для перехода к свойствам/полям в моем классе блоков данных. Поскольку все классы блоков данных являются производными/наследуемыми от базового класса (который содержит свойство Size), я могу использовать общую переменную базового класса типа для достаточно простого создания объекта в моем приложении; Я также могу получить/установить свойства на верхнем уровне. Моя проблема возникает, когда свойство является полем — я не знаю, как перейти на следующий уровень в поле, чтобы перейти к базовым свойствам и/или полям, если это применимо.

Мой базовый класс:

namespace MyBase {
   public class BaseClass {
       private int _size;
       public BaseClass() { }
       public BaseClass(int size) {
           _size = size;
       }
       public int Size() {
           return _size; 
       }
   }
}

Класс блока данных №1:

namespace DataBlock_class {
    //Data block class #1: (contains simple properties - will be used later)
    public class RecordBlock1_class : MyBase.BaseClass {
        public byte Char { get; set; }
        public byte Color { get; set; }
        public RecordBlock1_class() : base(2) {
            Char = 0;
            Color = 0;
        }
    }

    //Data block class #2: (contains simple properties)
    public RecordBlock2_class : MyBase.BaseClass {
        public bool Boolean1 { get; set; }
        public byte Byte1 { get; set; }
        public short Short1 { get; set; }
        public ushort UShort1 { get; set; }
        public RecordBlock2_class() : base(11) {
            Boolean1 = false;
            Byte1 = 0;
            Short1 = 0;
            UShort1 = 0;
        }
    }
    //Data block class #3: (contains simple properties & fields)
    public RecordBlock3_class : MyBase.BaseClass {
        public int Int1 { get; set; }
        public uint UInt1 { get; set; }
        public RecordBlock1_class[] ArrayField1 { get; set; }  // array of 12
        public RecordBlock1_class[] ArrayField2 { get; set; }  // array of 12
        public RecordBlock1_class[] ArrayField3 { get; set; }  // array of 12
        public RecordBlock2_class() : base(34) {
            Int1 = 0;
            UInt1 = 0;
            ArrayField1 = new RecordBlock1_class[12];
            for(int x = 0; x < 12; x++) {
                ArrayField1[x] = new RecordBlock1_class();
            }
            ArrayField2 = new RecordBlock1_class[12];
            for(int x = 0; x < 12; x++) {
                ArrayField2[x] = new RecordBlock1_class();
            }
            ArrayField3 = new RecordBlock1_class[12];
            for(int x = 0; x < 12; x++) {
                ArrayField3[x] = new RecordBlock1_class();
            }
        }
    }
}

Поскольку все мои классы блоков данных происходят/наследуются от MyBase.BaseClass, я могу использовать это для своей переменной - я не знаю, какой тип класса блока данных я буду обрабатывать во время выполнения.

в моем приложении С# у меня есть следующий блок кода:

string CSharpQualifiedName = "<this could be any of the data block classes above>";
// DataBlock_class.RecordBlock1_class
// DataBlock_class.RecordBlock2_class
// DataBlock_class.RecordBlock3_class

Используя мою переменную MyBase.BaseClass, я могу создать экземпляр объекта MyBase.BaseClass:

MyBase.BaseClass baseClass = null;
Type baseClassType = Type.GetType(CSharpQualifiedName);
if(baseClassType == null) {
    foreach(Assembly asm in AppDomain.CurrentDomain.GetAsseblies()) {
        baseClassType= asm.GetType(CSharpQualifiedName);
        if(baseClassType != null) {
            baseClass = Activator.CreateInstance(baseClassType) as MyBase.BaseClass;
            break;
        }
    }
}

Работать с первыми двумя классами блоков данных достаточно просто — я могу использовать PropertyInfo для получения/установки значений.

string fieldProperty = "<any property in the class>";
PropertyInfo pi = baseClass.GetType().GetProperty(fieldProperty);

Теперь моя проблема/проблема - RecordBlock3_class. Как мне добраться до одного из элементов в любом из полей/свойств массива И затем к свойству Char/Color в RecordBlock1_class???

Я могу использовать FieldInto для доступа к полям ArrayFieldX, но после этого я теряюсь?

FieldInfo fi = baseClass.GetType().GetField(fieldProperty);

Любая помощь/совет приветствуется!! Я скажу еще одну вещь: классы блоков данных могут стать немного более сложными, поскольку пользователи создают больше структур вложенных классов.


person Lorentz    schedule 12.11.2013    source источник


Ответы (1)


Вы также можете получить тип элемента свойства массива с помощью Reflection, а затем получить его свойства в обычном режиме:

string fieldProperty = "ArrayField1";
System.Reflection.PropertyInfo pi = baseClass.GetType().GetProperty(fieldProperty);
if (pi.PropertyType.IsArray)
{
    Type elementType = pi.PropertyType.GetElementType();
    System.Reflection.PropertyInfo pi2 = elementType.GetProperty("Color");
}

Основываясь на этом, вы можете написать простую, но более общую функцию, которая проходит через вложенные свойства (чтобы использовать также поля, просто измените приведенный ниже код):

static System.Reflection.PropertyInfo GetProperty(Type type, string propertyPath)
{
    System.Reflection.PropertyInfo result = null;
    string[] pathSteps = propertyPath.Split('/');
    Type currentType = type;
    for (int i = 0; i < pathSteps.Length; ++i)
    {
        string currentPathStep = pathSteps[i];
        result = currentType.GetProperty(currentPathStep);
        if (result.PropertyType.IsArray)
        {
            currentType = result.PropertyType.GetElementType();
        }
        else
        {
            currentType = result.PropertyType;
        }
    }
    return result;
}

а затем вы можете «запросить» объекты с «путями»:

PropertyInfo pi = GetProperty(c1.GetType(), "ArrayField1/Char");
PropertyInfo pi2 = GetProperty(c2.GetType(), "Color");

Если вы хотите получить значения объекта таким образом, метод будет аналогичен:

static object GetPropertyValue(object obj, string propertyPath)
{
    System.Reflection.PropertyInfo result = null;
    string[] pathSteps = propertyPath.Split('/');
    object currentObj = obj;
    for (int i = 0; i < pathSteps.Length; ++i)
    {
        Type currentType = currentObj.GetType();
        string currentPathStep = pathSteps[i];
        var currentPathStepMatches = Regex.Match(currentPathStep, @"(\w+)(?:\[(\d+)\])?");
        result = currentType.GetProperty(currentPathStepMatches.Groups[1].Value);
        if (result.PropertyType.IsArray)
        {
            int index = int.Parse(currentPathStepMatches.Groups[2].Value);
            currentObj = (result.GetValue(currentObj) as Array).GetValue(index);
        }
        else
        {
            currentObj = result.GetValue(currentObj);
        }

    }
    return currentObj;
}

И тогда вы можете получить запросы значений, включая массивы, например:

var v = GetPropertyValue(baseClass, "ArrayField1[5]/Char");

Конечно, оба метода требуют некоторой доводки обработки ошибок и т.д.

person Konrad Kokosa    schedule 12.11.2013
comment
Спасибо за ответ - один вопрос: когда я обрабатываю объект класса RecordBlock3_class, как я могу отличить ArrayField1[x].Char // где x может быть 0 .. 21??? - person Lorentz; 13.11.2013
comment
Почему вы хотите это сделать? Я имею в виду - с точки зрения типов они все одинаковы. Например, ArrayField1[3].Char или ArrayField1[10].Char предоставят ту же информацию. Или, может быть, вы хотите использовать эту информацию, чтобы получить фактическую стоимость этих свойств? - person Konrad Kokosa; 13.11.2013
comment
Я добавил в ответ способ получения значений аналогичным образом. - person Konrad Kokosa; 13.11.2013
comment
По сути, каждый ArrayField1 представляет отображаемый символ — Char (код символа) и Color (цвет символа); поэтому ArrayField1 будет иметь 12 отображаемых символов... и поэтому мне нужно иметь возможность получать/устанавливать каждый по отдельности. Еще раз спасибо за руководство. - person Lorentz; 13.11.2013
comment
Таким образом, вы можете использовать GetPropertyValue, написанный выше, чтобы получить значения и написать соответствующий метод установки. - person Konrad Kokosa; 13.11.2013
comment
Спасибо за все - я получил то, что мне нужно. Следующая строка кода дает мне значение поля массива: Array arr = (Array)result.GetValue(baseClass); // когда результатом является ArrayFieldX. - person Lorentz; 13.11.2013