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, я могу использовать это для своей переменной - я не знаю, какой класс блоков данных я буду обрабатывать во время выполнения.
в моем приложении C# у меня есть следующий блок кода:
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);
Любая помощь / совет очень ценится! Я скажу еще одну вещь, классы блоков данных могут стать немного более сложными, поскольку пользователи создают больше вложенных структур классов.
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");
Конечно, оба метода требуют некоторой полировки обработки ошибок и т. Д.