Downcasting в C#

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

Я пишу приложение, которое управляет объектами "Lead". (Это лиды продаж.) Одна часть моей программы будет импортировать ведет из текстового файла. Теперь текстовый файл содержит множество потенциальных версий, некоторые из которых я хочу импортировать, а некоторые-нет.

для удобства программирования (и использования) я анализирую текстовый файл в объект List и использую DataGridView для отображение лидов путем установки свойства DataSource объекта DataGridView.

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

моя первая мысль-вывести класс из свинца:

импорт общественного bool { get { возвращение bImport;} set { bImport = значение;} } }

однако механизм синтаксического анализа возвращает список Свинцовые объекты. Я не могу понизить вывод до LeadWithImportCheckbox. Это не удается: Это недопустимый бросок.

другой вариант, который я вижу, - создать конструктор для LeadWithImportCheckbox:

Это проблематично по двум причинам. Во-первых, ведущий объект имеет несколько десятков свойств, и запись этого конструктора является Пита.

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

есть ли лучший способ достижения моей цели?

11 ответов


или, чтобы избежать аспекта питы, используйте отражение... (попробовать это...)

EDIT: используйте свойство, а не поле, как я изначально написал...

public class NewLead : Lead
{
    public bool Insert;
    public NewLead(Lead lead, bool insert)
    {
        Insert = insert;
        foreach (PropertyInfo pi in typeof(Lead).GetProperties())
            GetType().GetProperty(pi.Name).SetValue
               (this, pi.GetValue(lead,null), null);
    }
}

public class LeadListItem
{
    public Lead Lead { get; set; }
    public bool ShouldImport { get; set; }
}

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

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


несколько вариантов, которые вы могли пропустить:

  • вы можете обновить сам объект Lead, чтобы иметь свойство Import (по умолчанию false).
  • вы можете использовать объект "ImportLead" для обработки свинца как полезной нагрузки (даже сделать его универсальным, если хотите), поэтому вам не нужен большой конструктор.
  • создайте новый список или перечисляемый объект, содержащий только объекты, которые вы хотите импортировать в первую очередь.

вы можете только downcast, если объект должен быть downcast действительно объект такого типа.

более простой способ решить вашу проблему - иметь класс DisplayLead, например:

  public class DisplayLead {
      Lead lead;
      bool bImport;
  }

что также поможет вам отделить сохраненные данные от их представления в GUI.


то, что вы хотите сделать, это отобразить столбец checkbox в вашей сетке и не иметь к нему никакого отношения. Вы используете отмеченные столбцы (и, возможно, исходный список) для создания нового набора списка, который будет вашим списком импорта.

затем обработайте все, что вы хотите сделать с недавно созданным списком.

Edit: одна вещь, чтобы быть осторожным при работе со списками является тот факт, что каждый объект класса на самом деле только указатель на класс, так что если вы работаете с оригинальный список и сделать что-то вроде:

List<Lead> Importable = new List<Lead>();

for(int i=0, i++, i<viewGrid.Count)
    if(viewGrid[i].CheckedColumn.Checked)
        Importable.Add(OriginalList[i]);

эти объекты будут существовать в обоих списках, и если вы измените данные свинца в любом списке, оба будут изменены.


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

Protip: Проверьте тип во время выполнения с помощью is оператора.


есть много способов сделать это, но "правильный" путь выскакивает из-за того, что вы сказали, здесь:

для простоты программирования (и использования), я разбор текстового файла на Объект List и использование DataGridView для отображения выводов установка свойства DataSource Практическое руководство.

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

код Lead объект хорошо стоит сам по себе, и вы хотите прикрепить к нему некоторые метаданные-вы не хотите создавать другой Lead классификация (т. е. LeadWithImportCheckbox класс).

Итак, лучший подход в вашем случае-иметь такой класс:

public class LeadInfo 
{
    private Lead lead;
    private bool shouldImport;

    public LeadInfo(Lead lead)
    {
        this.lead = lead;
        this.ShouldImport = false;
    }

    public bool ShouldImport 
    { 
        get { return shouldImport;  }
        set { shouldImport = value; }  
    }
}

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


Я видел правильное решение, перечисленное так много раз, я чувствую себя пяткой, публикующей его снова, но лучший способ подойти к этому-написать обертку для ведущего объекта, который включает флаг импорта.

Если свойства объекта Lead не отображаются в GridView, поскольку вы привязываете данные к объекту, то напишите свойства passthrough, которые отражают свойства Lead объекта-оболочки.

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

Если вы обеспокоены тем, что объект Lead будет меняться так много раз в будущем, что изменения в оболочке будут громоздкими, вы можете изучить динамическое генерирование кода на основе Объекта Lead, который будет автоматически генерировать объект-оболочку с теми же полями, что и объект Lead, плюс флаг импорта. Хотя, честно говоря, это намного больше работы, чем вам, вероятно, понадобится для чего-то такого простого, как это.


в качестве быстрого и грязного решения вы можете создать объект "checkbox" как другой объект, содержащий экземпляр Lead.

public GridLead {
   public bool Import { get; set; }
   public Lead Lead { get; set; }
}

таким образом, вы можете легко добавить больше свойств "сетки" к этому объекту, при этом всегда сохраняя ссылку на сведения о лидах без клонирования свойства hardcoding в него.


рекомендуется попробовать изменить (обновить) импортированные объекты lead.

попробуйте начать с примеров здесь...


Если у вашего класса Lead был конструктор копирования(например, "Lead (Lead otherLead)"), LeadWithImportCheckbox унаследует это, и вы можете просто вызвать базовый конструктор Lead в конструкторе LeadWithImportCheckbox - следовательно, нет необходимости LeadWithImportCheckbox знать подробности Lead.