Создание экземпляров вложенных классов с помощью отражения с #

У меня есть DAL, который обращается к нескольким файлам CSV, которые постоянно обновляют свои форматы и постоянно добавляют новые форматы.

каждая строка в CSV-файле представляет класс в системе, но из-за множества форматов ввод обрабатывается с помощью двухэтапного процесса.

Шаг 1. Преобразуйте строку из файла CSV в список значений. Преобразуйте этот список значений в «сырой» объект.

Этот необработанный объект затем будет отображен в объект бизнес-логики позже, поэтому все необработанные объекты будут преобразованы в объекты бизнес-логики.

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

Объект сопоставления - это атрибут с индексной позицией полей значений, присвоенный атрибутом свойства.

public static T BuildRawObject(string[] values)
        {
            T target = (T) Activator.CreateInstance(typeof(T), new object[] { });

            Type targetInfo = target.GetType();
            foreach (PropertyInfo pi in targetInfo.GetProperties())
            {
                if (!pi.PropertyType.IsValueType)
                {


                    if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj1)))
                    {
                        var result = BuildRawObject2<NestedObj1>(values);
                        try
                        {
                            pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on property: " + pi.PropertyType +
                                              " was bit not to set built instance on property. Instace was: " + result);
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                    else if(pi.PropertyType.IsAssignableFrom(typeof(NestedObj2)))
                    {
                        var result = BuildRawObject2<NestedObj2>(values);
                        try
                        {
                            pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on property: " + pi.PropertyType +
                                              " was bit not to set built instance on property. Instace was: " + result);
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                    else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj3)))
                    {
                        var result = BuildRawObject2<NestedObj3>(values);
                        try
                        {
                            pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on property: " + pi.PropertyType +
                                              " was bit not to set built instance on property. Instace was: " + result);
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                    else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj5)))
                    {
                        var result = BuildRawObject2<NestedObj5>(values);
                        try
                        {
                            pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on property: " + pi.PropertyType +
                                              " was bit not to set built instance on property. Instace was: " + result);
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                    else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj6)))
                    {
                        var result = BuildRawObject2<NestedObj6>(values);
                        try
                        {
                            pi.SetValue(target, result);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on property: " + pi.PropertyType +
                                              " was bit not to set built instance on property. Instace was: " + result);
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                    else if (pi.PropertyType.IsAssignableFrom(typeof(NestedObj7)))
                    {
                        var result = BuildRawObject2<NestedObj7>(values);
                        try
                        {
                            pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on property: " + pi.PropertyType +
                                              " was bit not to set built instance on property. Instace was: " + result);
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                    else
                    {
                       Console.WriteLine("An unrecognized class exists in the raw object. This will not be mapped to: " + pi.Name);
                    }

                }
                else
                {
                    Mapping mapping = pi.GetCustomAttribute<Mapping>();
                    if (mapping != null)
                    {
                        values[mapping.SourceIndex] = EmptyStringValuesWhenEmpty(values[mapping.SourceIndex]);
                        if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
                        {
                            bool valid = DateTime.TryParse(values[mapping.SourceIndex], out DateTime dateTime);
                            if (valid)
                            {
                                pi.SetValue(target, dateTime, null);
                            }
                            else
                            {
                                Console.WriteLine("Value: " + values[mapping.SourceIndex] + " on position[" +
                                                  mapping.SourceIndex + "] was not a valid date time. Value is required.");
                            }
                        }
                        else
                        {
                            try
                            {
                                pi.SetValue(target, GenericConverter(values[mapping.SourceIndex], pi.PropertyType), null);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine("Exception on index: " + mapping.SourceIndex + " contained value: " +
                                                  values[mapping.SourceIndex] + " was attempted for property type: " +
                                                  pi.PropertyType +
                                                  "Parse was not possible. Either input file is in wrong format, or RawInputObject has incorect property");
                                Console.WriteLine("Exception mesessage was: " + e.Message);
                                if (e.InnerException != null)
                                {
                                    Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                                }
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("Mapping to field: " + pi.Name +
                                          " was skipped due to missing mapping attribute definition.");
                    }
                }
            }
            return target;
        }

Проблема возникает, когда я пытаюсь присвоить значения вложенным классам для «Необработанного возвращаемого объекта».

в идеале я бы предпочел сделать все это рекурсивным методом. Однако родительский класс не того же типа, что и вложенные классы. Поэтому мне нужно создать новый метод, который делает то же самое, что и родительский класс, но принимает вложенные классы.

Однако создание, теоретически, бесконечного списка объектов именованного типа, которые могут встречаться в универсальном методе, просто ... неправильно. Я бы предпочел просто позволить программе сделать вывод из любого специализированного вложенного класса, переданного методу, чтобы определить, каким будет тип возвращаемого значения метода. Это возможно? Я не могу понять как.

Если я не совсем понимаю свой вопрос или у вас есть предложения, как я мог бы задать вопрос лучше, дайте мне знать. Если подробности отсутствуют. Пожалуйста, посоветуйте, и я дам.

Изменить: метод после предоставления решения

public static object BuildRawObject(Type objectType, string[] values)
    {
        dynamic target = Activator.CreateInstance(objectType, new object[] { });

        Type targetInfo = target.GetType();
        foreach (PropertyInfo pi in targetInfo.GetProperties())
        {
            if (pi.PropertyType.GetInterfaces().AsEnumerable().Contains(typeof(IRawObjectBase))  || pi.PropertyType.GetInterfaces().AsEnumerable().Contains(typeof(IRawNestedObjectBase)))
            {
                var result = BuildRawObject(pi.PropertyType, values);
                try
                {
                    pi.SetValue(target, Convert.ChangeType(result, pi.PropertyType));
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception on property: " + pi.PropertyType +
                                      " was bit not to set built instance on property. Instace was: " + result);
                    Console.WriteLine("Exception mesessage was: " + e.Message);
                    if (e.InnerException != null)
                    {
                        Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                    }
                }

            }
            else
            {
                Mapping mapping = pi.GetCustomAttribute<Mapping>();
                if (mapping != null)
                {
                    values[mapping.SourceIndex] = EmptyStringValuesWhenEmpty(values[mapping.SourceIndex]);
                    if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
                    {
                        bool valid = DateTime.TryParse(values[mapping.SourceIndex], out DateTime dateTime);
                        if (valid)
                        {
                            pi.SetValue(target, dateTime, null);
                        }
                        else
                        {
                            Console.WriteLine("Value: " + values[mapping.SourceIndex] + " on position[" +
                                              mapping.SourceIndex + "] was not a valid date time. Value is required.");
                        }
                    }
                    else
                    {
                        try
                        {
                            pi.SetValue(target, GenericConverter(values[mapping.SourceIndex], pi.PropertyType), null);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Exception on index: " + mapping.SourceIndex + " contained value: " +
                                              values[mapping.SourceIndex] + " was attempted for property type: " +
                                              pi.PropertyType +
                                              "Parse was not possible. Either input file is in wrong format, or RawInputObject has incorect property");
                            Console.WriteLine("Exception mesessage was: " + e.Message);
                            if (e.InnerException != null)
                            {
                                Console.WriteLine("Inner Exception message was: " + e.InnerException.Message);
                            }
                        }
                    }
                }
                else
                {
                    Console.WriteLine("Mapping to field: " + pi.Name +
                                      " was skipped due to missing mapping attribute definition.");
                }
            }
        }
        return target;
    }

person Morten Bork    schedule 31.10.2017    source источник
comment
Если я правильно понял, можно поменять подпись на static object BuildRawObject(Type objectType, string[] values), а потом сделать BuildRawObject(pi.PropertyType, values)?   -  person Evk    schedule 31.10.2017
comment
Прости. Это было преждевременно. Это не решает проблемы с возвращаемым типом.   -  person Morten Bork    schedule 31.10.2017
comment
Можете прояснить вопрос о возвращаемом типе?   -  person Evk    schedule 31.10.2017
comment
Когда я пробую ваше решение, и возвращается первый вложенный класс, он выдает исключение. Тип возвращаемого объекта NestedObject1 не соответствует типу возвращаемого значения ClassThatHoldClassProperties.   -  person Morten Bork    schedule 31.10.2017
comment
Кидает на какую строчку?   -  person Evk    schedule 31.10.2017


Ответы (1)


Кажется, в этом случае вас укусит ненужное использование дженериков. Если вы измените свою подпись на

static object BuildRawObject(Type objectType, string[] values)

вы сможете передать тип собственности прямо там, без множества проверок типа собственности:

if (propertyIsNotPrimitiveType) {
    var result = BuildRawObject(pi.PropertyType, values);
}

Для удобства вы можете оставить общую перегрузку:

static T BuildRawObject<T>(string[] values) {
     return (T) BuildRawObject(typeof(T), values);
}
person Evk    schedule 31.10.2017
comment
Это было именно то, к чему я стремился. Я не могу отблагодарить вас за вашу проницательность. Это была уловка! - person Morten Bork; 31.10.2017