Шаблон посетителя - это механизм для симуляции двойного связывания в языках программирования, которые поддерживают только одиночное связывание. К сожалению, это утверждение не может многое прояснить, поэтому позвольте мне объяснить на простом примере.
В используемой платформе .NET и C # объекты могут быть преобразованы в строки с помощью ToString()
функции. То, что делает эта функция, т.е. выполняемый код, зависит от типа объекта, к которому вы применяете ее (это виртуальный метод). То, какой код выполняется, зависит от одной вещи, одного типа объекта, поэтому используемый механизм называется одиночным связыванием.
Но что, если я хочу иметь более одного способа преобразования объекта в строку для каждого объекта другого типа? Что если бы я хотел иметь два способа преобразования объектов в строки, чтобы выполняемый код зависел от двух вещей: не только от объекта, который нужно преобразовать, но и от способа, которым мы хотим, чтобы он был преобразован?
Это можно было бы решить, если бы у нас было двойное связывание. Но большинство ОО-языков, включая C #, поддерживают только одну привязку.
Шаблон посетителя решает проблему, превращая двойное связывание в два последовательных одиночных связывания.
В нашем примере выше для преобразования используется виртуальный метод в объекте, который вызывает второй виртуальный метод в объекте, реализующем алгоритм преобразования.
Но это подразумевает, что объект, к которому вы хотите применить алгоритм, должен сотрудничать с этим: он должен иметь встроенную поддержку шаблона посетителя.
Похоже, вы используете классы .NET Windows Forms, которые не поддерживают шаблон посетителя. Точнее говоря, у них должен быть public virtual void Accept(IVisitor)
метод, которого, очевидно, у них нет.
Итак, какова альтернатива? Ну, .NET не только поддерживает одиночное связывание, но также поддерживает динамическое связывание, которое даже более эффективно, чем двойное связывание.
Для получения дополнительной информации о том, как применить эту технику, которая позволит вам решить вашу проблему (если я хорошо понимаю), посмотрите на Прощального посетителя .
ОБНОВИТЬ:
Чтобы применить метод к вашей конкретной проблеме, сначала определите метод расширения:
public static XmlDocument ToXml(this Control control)
{
XmlDocument xml = new XmlDocument();
XmlElement root = xml.CreateElement(control.GetType().Name);
xml.AppendChild(root);
Visit(control, xml, root);
return xml;
}
Создайте динамический диспетчер:
private static void Visit(Control control, XmlDocument xml, XmlElement root)
{
dynamic dynamicControl = control; //notice the 'dynamic' type.
//this is the key to dynamic dispatch
VisitCore(dynamicControl, xml, root);
}
Затем заполните конкретные методы:
private static void VisitCore(Control control, XmlDocument xml, XmlElement root)
{
// TODO: specific Control handling
}
private static void VisitCore(ContainerControl control, XmlDocument xml, XmlElement root)
{
// call the "base" method
VisitCore(control as Control, xml, root);
// TODO: specific ContainerControl handling
// for example:
foreach (Control child in control.Controls)
{
XmlElement element = xml.CreateElement(child.GetType().Name);
root.AppendChild(element);
// call the dynamic dispatcher method
Visit(child, xml, element);
}
}
private static void VisitCore(Form control, XmlDocument xml, XmlElement root)
{
// call the "base" method
VisitCore(control as ContainerControl, xml, root);
// TODO: specific Form handling
}