如何自动生成每个方法都必须编写的代码?

发布于 2024-09-12 16:52:55 字数 617 浏览 2 评论 0原文

我正在开发一个项目,我编写的每个方法都以相同的方式开始:

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
}

每个方法的最终结果都不同,具体取决于它的作用,但我总是从相同的结构开始。有人可以告诉我是否有一种方法可以自动编写这段代码,这样就不会那么重复了?也许“插入片段”?如果是这样,我如何定义片段?谢谢。

I'm working on a project and every single method I write starts off identical to:

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
}

Every method ends up differently depending on what it does, but I always start with the same structure. Can somebody tell me if there's a way to automate writing this code so it's not so repetitive? Maybe "Insert Snippet"? If so, how do I define snippets? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

陌若浮生 2024-09-19 16:52:56

我同意 @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();
    }
}

话虽如此,只是为了给您提供一种替代方案,我在类似情况下所做的一件事(尽管不是针对每种方法)是使用 例如,可以使用 lambda 来调用:

DoMethod(() => { return 5; }, ex => { return 0; })

I would agree with @Kent and the others that AoP looks good here, and also with @sleske that having to do this on every method suggests you might have design issues.

Having said that, just to give you an alternative, one thing I did in a similar situation (although not for every method) was to define a method with the boilerplate code in, and pass the 'useful' code in via a delegate:

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();
    }
}

Which could be called, for example, with a lambda:

DoMethod(() => { return 5; }, ex => { return 0; })
不忘初心 2024-09-19 16:52:56

有各种可用的代码片段编辑器,我都没有使用过,但我之前在 Codeplex 上看到过一个: 片段编辑器

There's various snippet editors available, none of which I've ever used but there is one on Codeplex that I've seen mentioned previously: Snippet Editor.

困倦 2024-09-19 16:52:56

您可以在 MSDN 上查看此链接了解如何创建/使用片段。我同意@Kent,尽管 AOP 解决方案是最好的。

You can look at this link on MSDN for how to create/use snippets. I agree with @Kent though that an AOP solution would be best.

怂人 2024-09-19 16:52:56

这是您可能想要的一个片段示例。
只需创建一个 .snippet 文件并将其放入 snippets 目录中。

<?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>

Here is a snippet example of what you might want.
Just create a .snippet file and put it in the snippets directory.

<?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>
洒一地阳光 2024-09-19 16:52:56

我不同意这里提出的关于日志记录的有用性以及为每个功能使用固定模板的问题的观点。

基于 Jerry Dennany 的文章 TraceListeners 和 Reflection,我扩展了 < a href="http://www.theobjectguy.com/" rel="nofollow">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>

I beg to differ with opinions raised here about the usefulness of logging, and about the problems of using a fixed template for each function.

Base on the Jerry Dennany's article TraceListeners and Reflection, I've extended the Object Guy's logging framework to produce an indented trace of my code. This can further be processed by logging tools but I don't bother - It's sometimes very constructive just to scan the results.

Of course, using aspect programming is the correct thing to do, but I never got to learning it. so I'm uisng the following snippet inside each and every of my methods to log, to verify each method's arguments, and to catch exceptions (which, by default, are re-thrown)

<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>

吹泡泡o 2024-09-19 16:52:55

您可以查看 AoP 解决方案,例如 PostSharp

You could look at an AoP solution, such as PostSharp.

抚你发端 2024-09-19 16:52:55

您可以使用“插入片段”,但我认为最好使用 PostSharp 之类的东西将其实现为横切,而不是到处都有重复的代码。

你会做这样的事情,这将有效地生成上面的代码:

public class TraceAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionEventArgs eventArgs)
    { 
        Tracing.StartOfMethod("Repository");
    }

    public override void OnExit(MethodExecutionEventArgs eventArgs)
    { 
        Tracing.EndOfMethod();
    }
}

You could use "Insert Snippet", but I would think it would be preferable to use something like PostSharp to implement this as cross-cut rather than have the duplicated code everywhere.

You would do something like this, which would effectively generate the code you have above:

public class TraceAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionEventArgs eventArgs)
    { 
        Tracing.StartOfMethod("Repository");
    }

    public override void OnExit(MethodExecutionEventArgs eventArgs)
    { 
        Tracing.EndOfMethod();
    }
}
洛阳烟雨空心柳 2024-09-19 16:52:55

正如 Kent Boogaart 指出的那样,这正是面向方面编程的发明目的。因此,您可以使用它,或者仅使用一些代码片段机制将代码作为模板插入到 IDE 中。

然而,我想说的是,在我看来,这是一种非常糟糕的编程风格。像这样的样板代码对于未来的维护和代码的可读性来说是一个沉重的负担。

我敦促您考虑其他选择。至少,您可以将样板代码放入“启动器”方法中,并让所有方法调用通过启动器方法传递,该方法会处理此问题。

在这种情况下,您的样板本身已经让我觉得非常有问题:

  • 跟踪每个方法调用似乎有点矫枉过正。如果您确实需要这个,只需附加一个调试器,这就是它们的用途。如果您想跟踪生产中的内容,那么拥有有意义的日志消息会更有帮助(“开始 frob 处理...”、“已处理 x glurg 对象”)。这些也将对系统管理员等有所帮助,而您的消息只有在您知道源代码时才有用。
  • 在我看来,在每种方法中捕获所有异常是完全不可原谅的。例外的一点是你可以让它们传播。仅在本地捕获您可以单独处理的异常;其余的应该转到调用堆栈上的通用处理程序。

也许您可以解释一下您编写此代码的动机?

As Kent Boogaart points out, this is just the scenario that Aspect Oriented Programming was invented for. So you can either use that, or just use some snippet mechanism to insert the code as a template in your IDE.

However, I would like to say that in my opinion this is a very bad programming style. Having boilerplate code like this is a heavy burden for future maintenance and readability of the code.

I urge you to consider other options. At the least, you could put the boilerplate code into a "launcher" method, and have all methods call pass via the launcher method, which takes care of this.

In this case, your boilerplate itself already strikes me as very problematic:

  • Tracing every method call seems like overkill. If you really need this, just attach a debugger, that's what they are for. If you want to trace stuff in production, it's way more helpful to have meaningful log messages ("Starting frob processing...", "processed x glurg objects"). These will also help e.g. sysadmins, whereas your messages are only useful if you know the source code.
  • Catching all exceptions in every method is in my opinion totally inexcusable. The point of exceptions is that you can let them propagate. Only catch locally the exceptions which you can handle individually; the rest should go to a generic handler up the call stack.

Perhaps you could explain your motivation for this code?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文