类中的方法是否可以继承在方法的开头和结尾执行的子例程?

时间:2018-04-04 20:44:47

标签: c# logging c#-4.0

我有一个记录器,我正在添加到我的项目中。现在,对于每个方法,我必须在每个方法的开头写入Logger.DebugLog(" Starting Method")和Logger.DebugLog("完成方法")

这个记录器 - 当启用Debug时 - 允许我准确地跟踪在这次运行中调用的方法,这样如果出现问题,我可以看到它在破坏之前有多远,使其易于调试。假设正在捕获方法名称和行 - 我的目标是我不想在+100公共或私人方法的每一个上添加这两行

namespace myProject
{
   public class myClass
   {
     public bool MyPublicMethod(string Message = "someRandomMessage")
     {
        try
        {
            myPrivateMethod(1);
            writeToLog(Message);
            return true;
        }
        catch(){
           return false; 
        }
     }

     private bool myPrivateMethod(int passedNumber)
     {
        try
        {
            writeToLog(passedNumber);
            return true;
        }
        catch(){
           return false;
        }
     }
   }
}

我的日志文件应如下所示:

04:00:00 - Starting Method:MyPublicMethod
04:00:00 - Starting Method:myPrivateMethod
04:00:01 - 1
04:00:01 - Completed Method:myPrivateMethod
04:00:02 - someRandomMessage
04:00:02 - Completed Method:MyPublicMethod

我现在被迫做的事情看起来很混乱:

namespace myProject
{
   public class myClass
   {
     public bool MyPublicMethod(string Message = "someRandomMessage")
     {
        try
        {
            writeToLog("Starting Method");
            myPrivateMethod(1);
            writeToLog(Message);

            writeToLog("Completed Method");
            return true;
        }
        catch(){
           return false; 
        }
     }

     private bool myPrivateMethod(int passedNumber)
     {
        try
        {
            writeToLog("Starting Method");
            writeToLog(passedNumber);

            writeToLog("Completed Method");
            return true;
        }
        catch(){
           return false;
        }
     }
   }
}

这在.NET中是否可行,或者如果我想使用它,我是否必须明确列出该日志记录方法?

4/6/18编辑:有可能 - 请参阅AOP。这是一篇相当不错的文章http://www.dotnetcurry.com/patterns-practices/1305/aspect-oriented-programming-aop-csharp-using-solid 以下是我正在寻找的确切摘要:

  

请考虑以下代码:

public class DocumentSource : IDocumentSource
{
    //..     
    public Document[] GetDocuments(string format)
    {
        try
        {
            using (var context = CreateEFContext())
            {
                var documents =
                    context
                        .Documents
                        .Where(c => c.Name.EndsWith("." + format))
                        .ToArray();

                logger.LogSuccess(
                    "Obtained " + documents.Length + " documents of type " + format +
                    Environment.NewLine +
                    "Connection String: " + connectionString);

                 return documents;
            }
        }
        catch (Exception ex)
        {
           logger.LogError(
               "Error obtaining documents of type " + format +
               Environment.NewLine +
               "Connection String: " + connectionString, ex);

            throw;
        }
    }     
    //..
}
  

以下是该方法在没有记录的情况下的样子:

public Document[] GetDocuments(string format)
{
    using (var context = CreateEFContext())
    {
        return
            context
                .Documents
                .Where(c => c.Name.EndsWith("." + format))
                .ToArray();
    }
}
  

显然,日志记录代码使原始方法的可读性降低。它将真实的方法代码与日志代码纠缠在一起   这也违反了单一责任原则   此外,我们希望在整个代码库中的许多方法中找到相同的日志记录模式。基本上,我们希望找到以下模式:

try
{
    //Do something here

    logger.LogSuccess(…                
    //..
}
catch (Exception ex)
{
    logger.LogError(…
    throw;
}

2 个答案:

答案 0 :(得分:1)

有一些Fody加载项允许您在编译时将这种代码添加到编译输出中,而不必自己编写。

例如,MethodDecorator允许您定义特定属性,使用该属性修饰的任何方法都会在进入和退出方法之前调用特定方法。

我应该注意,对于任何合理大小的项目,记录每个方法的入口和出口将产生比任何人合理期望读取的更多日志消息。我建议您明智地了解哪些方法添加了日志消息,以及您在这些日志消息中包含哪些信息。

绝大多数情况下,一个更有用的策略是使用保护语句来测试你的假设,在任何不合适的时刻抛出异常,然后用更有用的信息包装异常(通过{{1当它们上升到调用链时,最后在应用程序的顶层记录这些异常的结果。这样,只有当某些内容以您不希望的方式出现时,您才会生成日志消息,并且该情况下的日志消息包含您可能需要的所有信息。

答案 1 :(得分:0)

我不确定这正是您正在寻找的,但它可能会指向您正确的方向。出于演示目的,我已登录到控制台,但您可以改为登录文件。

您可以创建一个方法来接受另一个执行的方法,该方法执行该函数并返回该值,并通过写入日志文件来包装方法调用的开始和结束:

// For methods that return a value
private static TResult LogMethod<TResult>(string displayName, Func<TResult> method)
{
    Console.WriteLine($"{DateTime.Now} - Starting method: {displayName}");
    TResult result = method();
    Console.WriteLine($"{DateTime.Now} - Completed method: {displayName}");
    return result;
}

// For void methods
private static void LogMethod(string displayName, Action method)
{
    Console.WriteLine($"{DateTime.Now} - Starting method: {displayName}");
    method();
    Console.WriteLine($"{DateTime.Now} - Completed method: {displayName}");
}

作为如何使用它的一个例子,我们说我们有以下方法:

private static int GetNumberFromUser(string prompt)
{
    int result;
    do
    {
        Console.Write(prompt);
    } while (!int.TryParse(Console.ReadLine(), out result));
    return result;
}

private static int AddNumbers(int first, int second)
{
    return first + second;
}

private static void Tell(string message)
{
    Console.WriteLine(message);
}

现在,我们不是直接调用这些方法,而是通过LogMethod函数调用它们:

private static void Main()
{
    var firstNumber = LogMethod("GetNumber", 
        () => GetNumberFromUser("Enter first number: "));

    var secondNumber = LogMethod("GetNumber", 
        () => GetNumberFromUser("Enter second number: "));

    var result = LogMethod("AddNumber", 
        () => AddNumbers(firstNumber, secondNumber));

    LogMethod("Tell", () => Tell($"{firstNumber} + {secondNumber} = {result}"));

    GetKeyFromUser("\nDone!! Press any key to exit...");
}

<强>输出

enter image description here