Мне нравится подход посетителя выше. Однако просто для удовольствия я покажу, как уменьшить избыточность в коде, используя T4 в VS2008 и VS2010.
Избыточность исходит из того, что для каждого сообщения вам нужен метод Visit. Также каждому методу нужна простая, но избыточная реализация Accept. Один из способов приблизиться к «Не повторяйся» - это сгенерировать код, используя T4.
Чтобы протестировать следующий пример, добавьте класс в VS, но измените расширение с .cs на .tt. Теперь вы получите два файла .tt-файл и .cs-файл, подключенные к .tt-файлу.
Файл .tt - это шаблон, который генерирует файл .cs. В то время они идентичны.
Используйте это как содержимое файла .tt:
<#@ template language="C#" #>
<#
// On VS2008 change C# above to C#v3.5
// -----------------------------------------------------
// Here we declare our different message types
var messageTypes = new []
{
"Simple",
"Complex",
"Other",
};
// -----------------------------------------------------
#>
namespace MessageProcessor
{
partial interface IMessageVisitor
{
<#
// Let's generate all message visitor methods
foreach (var messageType in messageTypes)
{
#>
void Visit (<#=messageType#>Message message);
<#
}
#>
}
abstract partial class Message
{
public abstract void Accept (IMessageVisitor visitor);
}
<#
// Let's generate all message types
foreach (var messageType in messageTypes)
{
#>
sealed partial class <#=messageType#>Message : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
<#
}
#>
}
Это должно сгенерировать CS-файл, который выглядит следующим образом:
namespace MessageProcessor
{
partial interface IMessageVisitor
{
void Visit (SimpleMessage message);
void Visit (ComplexMessage message);
void Visit (OtherMessage message);
}
abstract partial class Message
{
public abstract void Accept (IMessageVisitor visitor);
}
sealed partial class SimpleMessage : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
sealed partial class ComplexMessage : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
sealed partial class OtherMessage : Message
{
public override void Accept (IMessageVisitor visitor)
{
visitor.Visit (this);
}
}
}
Почему это менее избыточно? Потому что теперь, когда мне нравится добавлять новое сообщение, я просто добавляю его в шаблон:
var messageTypes = new []
{
"Simple",
"Complex",
"Other",
"YetAnotherOne",
};
Важно отметить, что все сообщения генерируются как частичные, потому что нам нужны разные полезные нагрузки для сообщения. Это указано в другом файле и может выглядеть так:
partial class SimpleMessage
{
public string Name;
}
partial class ComplexMessage
{
public XmlDocument Xml;
}
Для тех, кто любит звук T4, просмотрите этот блог: http://www.olegsych.com/2008/09/t4-tutorial-creatating-your-first-code-generator/