Как вы можете привести тип к типу, используя имя типа как строку? - PullRequest
7 голосов
/ 28 января 2011

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

Я пытаюсь использовать универсальный метод для загрузки форм в WinForms:

protected void LoadForm<T>(ref T formToShow, bool autoLoaded) where T : FormWithWorker, new()
{
    // Do some stuff
}

Формы загружаются с помощью ToolStripMenuItem (либо путем выбора элемента, либо с помощью пункта меню «Открыть Windows»). Они загружаются лениво, поэтому в формах родителя MDI есть поля для форм, но они пусты до тех пор, пока они не понадобятся. У меня есть общий метод, используемый для ToolStripMenuItem_Click, который обрабатывает все щелчки по пунктам меню. У метода нет реального способа узнать, для какой формы вызывается, за исключением того, что имя ToolStripMenuItem соответствует шаблону, выбранному для имен классов форм, которым они соответствуют. Таким образом, используя имя ToolStripMenuItem, я могу определить название типа запрашиваемой формы и имя частного поля, выделенного для хранения ссылки на эту форму.

Используя это, я могу либо использовать растущий / сжимающийся оператор switch с жестко закодированными типами и совпадениями строк для вызова метода с определенным набором типов (нежелательно), либо я могу использовать Reflection, чтобы получить поле и создать экземпляр тип. Для меня проблема в том, что System.Activator.CreateInstance предоставляет ObjectHandler, который не может быть приведен к нужным мне типам. Вот фрагмент того, что у меня есть:

string formName = "_form" + ((ToolStripMenuItem)sender).Name.Replace("ToolStripMenuItem", "");
string formType = formName.Substring(1);

FieldInfo fi = this.GetType().GetField(formName, BindingFlags.NonPublic | BindingFlags.Instance);

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad = (????)System.Activator.CreateInstance("MyAssemblyName", formType);
}

this.LoadForm(ref formToLoad, false);
fi.SetValue(this, formToLoad);

Я знаю строковое имя типа, который используется для (????), но во время компиляции я не знаю тип, потому что он изменяется. Я попробовал несколько способов заставить этот каст / инстанцирование работать, но ни один не был успешным. Мне бы очень хотелось узнать, возможно ли выполнить такое приведение, зная тип только как строку. Я пытался использовать Type.GetType(string, string) для выполнения приведения, но компилятору это не понравилось. Если у кого-то другое представление о том, как загружать формы динамически, потому что я просто делаю это глупо, пожалуйста, дайте мне знать об этом.

Ответы [ 4 ]

12 голосов
/ 28 января 2011

Эта проблема обычно решается путем приведения к общему базовому классу или интерфейсу всех потенциальных типов.

В C # 4 вы также можете назначить его переменной dynamic для хранения возвращаемого значения и вызовапроизвольные методы на это.Методы будут с опозданием.Однако я всегда предпочитаю придерживаться прежнего решения.

5 голосов
/ 28 января 2011

Вам будет лучше с другой перегрузкой , которая принимает Type и использует, например, Type.GetType(string).

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad =
      (FormWithWorker)System.Activator.CreateInstance(Type.GetType("MyNamespace.MyFormType"));
}
2 голосов
/ 28 января 2011

В зависимости от того, что у вас есть, FormWithWorker должен быть (как минимум) базовым классом типа, который вы создаете, поэтому вы можете сделать это:

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad = (FormWithWorker)System.Activator.CreateInstance("MyAssemblyName", formType);
}
0 голосов
/ 28 января 2011

Хотя общий интерфейс является одним из способов решения этой проблемы, интерфейсы не являются практичными для всех сценариев. Вышеупомянутое решение заключается в том, чтобы использовать фабричный шаблон (оператор switch - выбор конкретного класса) или использовать отражение. Есть стековый пост, который решает эту проблему. Я считаю, что вы можете напрямую применить это к вашей проблеме:

Метод фабрики - случай против отражения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...