1. Гость, мы просим Вас ознакомиться с Правилами Форума и Отказом от ответственности!

1.5.1 (101) C# Создать элемент в массиве с помощью рефлексии

Тема в разделе 'PW Вопросник', создана пользователем Hardx, 24 окт 2015.

  1. TopicStarter Overlay

    Hardx Программист Пользователи

    Сообщения:
    47
    Лайки:
    66
    Пол:
    Мужской
    Репутация:
    9
    Прив всем) Вопрос может быть и нубский, но ответа я в интернете не нашел.

    Есть идея читать пакеты принятые от игровых служб PW с помощью рефлексии и рекурсии.
    Допустим есть структура GRoleBase из Gamedbd:

    Код:
    public class GRoleBase
        {
            public byte version { get; private set;}
            public int id{ get; private set;}
            public Octets name{ get; private set;}
            public int race{ get; private set;}
            public int cls{ get; private set;}
            public byte gender{ get; private set;}
            public Octets custom_data{ get; private set;}
            public Octets config_data{ get; private set;}
            public int custom_stamp{ get; private set;}
            public byte status{ get; private set;}
            public int delete_time{ get; private set;}
            public int create_time{ get; private set;}
            public int lastlogin_time{ get; private set;}
            public GRoleForbid[] forbid{ get; private set;}
            public Octets help_states{ get; private set;}
            public int spouse{ get; private set;}
            public int userid{ get; private set;}
            public Octets cross_data{ get; private set;}
            public byte reserved2{ get; private set;}
            public byte reserved3{ get; private set;}
            public byte reserved4{ get; private set;}
        }
    В этой структуре есть массив из GRoleForbid, и каждый элемент в массиве содержит следующую структуру:

    Код:
    public class GRoleForbid
        {
            public byte type { get; private set;}
            public int time { get; private set;}
            public int createtime { get; private set;}
            public Octets reason { get; private set;}
        }
    И мой метод чтения:
    Код:
            public static void DoReflection(object cls, PacketReader packetReader)
            {
                foreach (var prop in cls.GetType().GetProperties())
                {
                    switch (prop.PropertyType.ToString())
                    {
                        case "System.Byte":
                            {
                                prop.SetValue(cls, packetReader.ReadByte());
                                break;
                            }
                        case "System.Int32":
                            {
                                prop.SetValue(cls, (int)packetReader.ReadInt32());
                                break;
                            }
                        case "SGR.Octets":
                            {
                                prop.SetValue(cls, packetReader.ReadOctets());
                                break;
                            }
                        default:
                            {
                                if (prop.PropertyType.IsArray)
                                {
                                    var length = packetReader.ReadCUInt32();
                                    object value = Activator.CreateInstance(prop.PropertyType, length);
    
                                    //prop.PropertyType равняется GRoleForbid[]
                                
                                    foreach (var item in (object[])value)
                                    {
                                        //Перед вызовом следующего метода, нужно создать экземпляр.
                                        DOReflection(item, packetReader);
                                    }
                             
                                    prop.SetValue(cls, value);
                                }
                                else
                                {
                                    object value = Activator.CreateInstance(prop.PropertyType);
                                    prop.SetValue(cls, value);
                                    DoReflection(value, packetReader);
                                }
                                break;
                            }
                    }
                }
            }
    Проблема возникла с создание экземпляра класса в массиве. Если брать по примеру, то создать экземпляр GRoleForbid. Но для создания нужно узнать сначала тип этого элемента, ведь этот метод будет читать любые структуры, отправленные в этот метод. Собственно в этом и заключается вопрос, как это можно сделать?

    P.S От безысходности уже хотел согрешить, и использовать велосипед, переведя тип массива в строку и убрать квадратные скобки. Но это уже, по моему мнению, какой-то ад получается.
    Последнее редактирование: 25 окт 2015
  2. JonMagon Программист Пользователи Open Source Contributor

    Сообщения:
    805
    Лайки:
    809
    Пол:
    Мужской
    Репутация:
    5
    Страна:
    Germany Germany
    В смысле ты хочешь затолкать объекты-наследников разных классов в один массив?
  3. TopicStarter Overlay

    Hardx Программист Пользователи

    Сообщения:
    47
    Лайки:
    66
    Пол:
    Мужской
    Репутация:
    9
    нет, я хочу чтобы мой метод(DoReflection) просмотрел все свойства в классе(допустим структура GRoleBase), и допустим если он встретил в классе(структура GRoleBase) свойство с типом "Int32", значит он присвоит этому свойству значение прочитанное из пакета, который нам прислал Gamedbd.
    Обычно читалось это так:
    Код:
    int id = PacketReader.ReadInt32();
    А массивы обычно читались так:
    Код:
                int lenght = PacketReader.ReadCUInt32();
                GRoleForbid[] forbid = new GRoleForbid[lenght];
    
                for (int i = 0; i < lenght; i++)
                {
                    forbid[i] = new GRoleForbid();
                    forbid[i].createtime = packetReader.ReadInt32();
                    ...
                }
    Так вот, если мой метод найдет массив, он будет создавать массив с нужным кол-во, и будет с помощью рекурсии читать все что находится в след. структуре. Только вот как инициализировать(forbid = new GRoleForbid() ; ) эту структуру с помощью рефлексии я не знаю
  4. JonMagon Программист Пользователи Open Source Contributor

    Сообщения:
    805
    Лайки:
    809
    Пол:
    Мужской
    Репутация:
    5
    Страна:
    Germany Germany
    Так и инициализируй. о_О
  5. TopicStarter Overlay

    Hardx Программист Пользователи

    Сообщения:
    47
    Лайки:
    66
    Пол:
    Мужской
    Репутация:
    9
    Если я явно начну указывать типы, тогда этот метод будет только для одной структуры.
    Вот обычный пример инициализации класса с помощью рефлексии:

    Код:
       
    //Допусти в нашей структуре, цикл нашел свойство "gRoleBase" с типом "GRoleBase"
    //prop = полностью всё наше свойство.
    //prop.PropertyType = GRoleBase
    
    //В объект value записывается инициализация класса GRoleBase
    object value = Activator.CreateInstance(prop.PropertyType);
    //Свойству gRoleBase устанавливается значение инициализации.
    prop.SetValue(cls, value);
    //Начинает читать все свойства, которые находятся в классе gRoleBase
    DoReflection(value, packetReader);
                               
    

    И это всё хорошо работает, но с массивами немного не катает. У массивов prop.PropertyType = GRoleforbid[], поэтому инициализировать массив я могу, а вот объекты внутри массива я не знаю как. Опять же, в методе DoReflection ничего не должно быть явного.
  6. int 3 Программист Пользователи Open Source Contributor

    Сообщения:
    342
    Лайки:
    531
    Пол:
    Мужской
    Репутация:
    10
    Страна:
    Russian Federation Russian Federation
    Если я правильно понимаю, ТС хочет сделать цикл чтения по всем свойствам класса. Насколько я знаю, ни один язык таких "циклов" не поддерживает. Хотя на плюсах/си можно просто отдать указатель на память и аккуратно по нему записать цельный блок данных. Теоретически, на шарпе в unsafe тоже так можно, но я не знаю сколько лишней инфы, помимо свойств, может содержать класс
    Rody66 и Hardx нравится это.
  7. JonMagon Программист Пользователи Open Source Contributor

    Сообщения:
    805
    Лайки:
    809
    Пол:
    Мужской
    Репутация:
    5
    Страна:
    Germany Germany
    Ну так ведь, ты это и хотел, выходит.
    Никак ты так не сделаешь, разве что через словарь (Dictionary), где ключ — название поля, а значение — его enum тип.
  8. TopicStarter Overlay

    Hardx Программист Пользователи

    Сообщения:
    47
    Лайки:
    66
    Пол:
    Мужской
    Репутация:
    9
    Сколько не старался рассказать свою идею, не получилось) Как найду решение свой проблемы, выложу исходники на форуме. Данное решение должно значительно упростить чтение/запись и значительно уменьшит объем кода. Спасибо всем)
  9. JoLan Команда форума Администратор AngeliCore Программист

    Сообщения:
    1.061
    Лайки:
    948
    Пол:
    Мужской
    Репутация:
    6
    Команда:
    AngeliCore
    Страна:
    Russian Federation Russian Federation
    А зачем хардкодить саму структуру? Как один из вариантов можно использовать конфиг, который будет подгружаться, основываясь на заголовке пакета.

    Код:
    if (Packet.Header == n)
    {
       Config.LoadFrom("n.xml");
       Config.Read(Packet);
    }
    Где Packet класс протокола общения, а Config - бинарный парсер.
  10. TopicStarter Overlay

    Hardx Программист Пользователи

    Сообщения:
    47
    Лайки:
    66
    Пол:
    Мужской
    Репутация:
    9
    Этот вариант хорош, если для вывода использовать словарь. Что меня не устраивает.
    А если засовывать в структуру, это уже куча кода, к тому-же и лишнее чтение файла. На этом методе работает Iweb и pwadmin от ronny
  11. gouranga Эксперт Программист Пользователи Open Source Contributor White List

    Сообщения:
    67
    Лайки:
    142
    Пол:
    Мужской
    Репутация:
    0
    Страна:
    Netherlands Netherlands
    я бы не рассматривал просто рефлексию — очень оно медленно. я генерировал чтение-запись в runtime, с помощью System.Reflection.Emit. получалось, что по факту методы Read и Write генерирует сама программа для каждого нужного класса (и дочерних) на старте один раз, а дальше выбирал нужный метод для нужного класса и выполнял. получалось практически одинаково по скорости как если бы это писать вручную.
    Rody66 нравится это.
  12. TopicStarter Overlay

    Hardx Программист Пользователи

    Сообщения:
    47
    Лайки:
    66
    Пол:
    Мужской
    Репутация:
    9
    Для меня runtime и мета-данные пока что еще что-то новое. Я так понял, мне нужно с помощью MethodBuilder`a и IL кода создать методы чтения и записи? Звучит всё как-то сложно)

    И да, если у кого-то будет такой же вопрос, как у меня в первом посте, вот решение:

    Код:
     if (prop.PropertyType.IsArray)
                                {
                                    var length = ReadCUInt32();
                                    var value = Activator.CreateInstance(prop.PropertyType, length);
    
                                    for (int i = 0; i < length; i++)
                                    {
                                        ((object[])value)[i] = Activator.CreateInstance(prop.PropertyType.GetElementType());
                                        Deserialization(((object[])value)[i]);
                                    }
                                       
                                    prop.SetValue(cls, value);
                                }
    Тыщ нравится это.
Черновик сохранён Черновик удалён
Similar Threads
  1. marcusx
    Ответов:
    9
    Просмотров:
    3.599
  2. tunes
    Ответов:
    3
    Просмотров:
    1.316
  3. gorgeous
    Ответов:
    3
    Просмотров:
    1.243
  4. • System
    Ответов:
    4
    Просмотров:
    2.918
  5. Denis-root
    Ответов:
    7
    Просмотров:
    4.535
Загрузка...

Поделиться этой страницей