Как автоматически генерировать код, который должен быть написан для каждого метода?
Я работаю над проектом, и каждый отдельный метод, который я пишу, начинается с:
public blah blah() // my method signature
{
Tracing.StartOfMethod("Repositroy");
// I'll declare variables as needed
try
{
// the content here differs for every method
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
// sometimes I'll add something here
}
finally
{
// sometimes something here
Tracing.EndOfMethod();
}
// return result
}
Каждый метод заканчивается по-разному в зависимости от того, что он делает, но я всегда начинаю с одной и той же структуры. Может кто-нибудь сказать мне, если есть способ автоматизировать написание этого кода, чтобы он не был таким повторяющимся? Может быть "Вставить фрагмент"? Если так, как я могу определить фрагменты? Благодарю.
8 ответов
Я бы согласился с @Kent и другими, что AoP выглядит здесь хорошо, а также с @sleske, что необходимость делать это для каждого метода предполагает, что у вас могут возникнуть проблемы с дизайном.
Сказав это, просто чтобы дать вам альтернативу, я сделал одну вещь в аналогичной ситуации (хотя и не для каждого метода), чтобы определить метод с шаблонным кодом и передать "полезный" код через делегата:
public T DoMethod<T>( Func<T> mainCode, Func<Exception, T> exceptionHandlerCode)
{
Tracing.StartOfMethod("Repository");
try
{
return mainCode.Invoke();
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
return exceptionHandlerCode.Invoke(ex);
}
finally
{
// sometimes something here
Tracing.EndOfMethod();
}
}
Который можно назвать, например, лямбда-выражением:
DoMethod(() => { return 5; }, ex => { return 0; })
Вы можете использовать "Вставить фрагмент", но я думаю, что было бы предпочтительнее использовать что-то вроде PostSharp для реализации этого в качестве сквозного, а не повсеместно дублировать код.
Вы бы сделали что-то вроде этого, что эффективно сгенерирует код, который у вас есть выше:
public class TraceAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
Tracing.StartOfMethod("Repository");
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Tracing.EndOfMethod();
}
}
Как указывает Кент Бугаарт, это именно тот сценарий, для которого было изобретено Аспектно-ориентированное программирование. Таким образом, вы можете использовать это или просто использовать некоторый механизм сниппета для вставки кода в качестве шаблона в вашу IDE.
Однако я хотел бы сказать, что, на мой взгляд, это очень плохой стиль программирования. Наличие такого стандартного кода является тяжелым бременем для будущего обслуживания и читабельности кода.
Я призываю вас рассмотреть другие варианты. По крайней мере, вы можете поместить шаблонный код в метод "запуска" и сделать так, чтобы все вызовы методов проходили через метод запуска, который позаботится об этом.
В этом случае сам твой шаблон уже кажется мне очень проблематичным:
- Отслеживание каждого вызова метода кажется излишним. Если вам это действительно нужно, просто подключите отладчик, вот для чего они. Если вы хотите отследить материал в производственном процессе, гораздо полезнее иметь значимые сообщения журнала ("Запуск обработки frob...", "обработанные объекты x glurg"). Это также поможет, например, sysadmins, тогда как ваши сообщения полезны, только если вы знаете исходный код.
- Поймать все исключения в каждом методе, на мой взгляд, совершенно непростительно. Исключением является то, что вы можете позволить им распространяться. Ловите только локально исключения, которые вы можете обрабатывать индивидуально; остальное должно идти к универсальному обработчику стека вызовов.
Возможно, вы могли бы объяснить свою мотивацию для этого кода?
Доступны различные редакторы фрагментов кода, ни один из которых я никогда не использовал, но есть один на Codeplex, о котором я уже упоминал ранее: редактор фрагментов.
Вот фрагмент кода того, что вы могли бы хотеть. Просто создайте файл.snippet и поместите его в каталог сниппетов.
<?xml version="1.0" encoding="utf-8"?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0">
<Header>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
<Title>commonmethodsnippet</Title>
<Shortcut>commonmethodsnippet</Shortcut>
<Description>Common method snippet.</Description>
<Author>Me</Author>
</Header>
<Snippet>
<Declarations>
<Literal Editable="true">
<ID>returnValue</ID>
<ToolTip>Return Value</ToolTip>
<Default>returnValue</Default>
<Function>
</Function>
</Literal>
<Literal Editable="true">
<ID>methodName</ID>
<ToolTip>Method Name</ToolTip>
<Default>methodName</Default>
<Function>
</Function>
</Literal>
<Literal Editable="true">
<ID>methodDescription</ID>
<ToolTip>Method Description</ToolTip>
<Default>methodDescription</Default>
<Function>
</Function>
</Literal>
<Literal Editable="true">
<ID>variableDeclarations</ID>
<ToolTip>Variable Declarations</ToolTip>
<Default>variableDeclarations</Default>
<Function>
</Function>
</Literal>
</Declarations>
<Code Language="csharp"><![CDATA[public $returnValue$ $methodName$() // $methodDescription$ {
Tracing.StartOfMethod("Repository");
// Variable Declarations
$variableDeclarations$
try
{
// the content here differs for every method
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
// sometimes I'll add something here
}
finally
{
// sometimes something here
Tracing.EndOfMethod();
}
// return result }]]></Code>
</Snippet> </CodeSnippet> </CodeSnippets>
Я позволю себе не согласиться с мнением, высказанным здесь о полезности ведения журнала и о проблемах использования фиксированного шаблона для каждой функции.
Основываясь на статье TraceListeners and Reflection, выполненной Джерри Деннани, я расширил каркас логирования Object Guy, чтобы получить след от моего кода. Это может быть дополнительно обработано средствами ведения журналов, но я не беспокоюсь - иногда очень конструктивно просто сканировать результаты.
Конечно, использование аспектного программирования - правильная вещь, но я так и не научился этому. поэтому я использую следующий фрагмент внутри каждого из моих методов для регистрации, проверки аргументов каждого метода и перехвата исключений (которые по умолчанию перебрасываются)
<CodeSnippet Format="1.0.0">
<Header>
<Title>Canonic</Title>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>ClassName</ID>
<ToolTip>Replace with the name of the class.</ToolTip>
<Default>ClassName</Default>
</Literal>
<Literal>
<ID>MethodName</ID>
<ToolTip>Replace with the name of the method.</ToolTip>
<Default>MethodName</Default>
</Literal>
<Literal>
<ID>FirstArgName</ID>
<ToolTip>Replace with the name of the first argument.</ToolTip>
<Default>FirstArgName</Default>
</Literal>
<Literal>
<ID>SecondArgName</ID>
<ToolTip>Replace with the name of the second argument.</ToolTip>
<Default>SecondArgName</Default>
</Literal>
<Literal>
<ID>ResultName</ID>
<ToolTip>Replace with the name of the result.</ToolTip>
<Default>ResultName</Default>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[ Logger.LogMethod("$FirstArgName$", $FirstArgName$,"$SecondArgName$", $SecondArgName$);
try
{
Validator.Verify($FirstArgName$, $SecondArgName$);
//VerifyFields();
Logger.LogReturn($ResultName$);
return $ResultName$;
}
#region Exception
catch (Exception exp)
{
Logger.LogException("$ClassName$.$MethodName$", exp);
throw;
}
#endregion Exception
]]>
</Code>
</Snippet>
Вы можете посмотреть эту ссылку на MSDN, чтобы узнать, как создавать / использовать фрагменты. Я согласен с @Kent, хотя решение AOP будет лучшим.