9

有人问我如何打印

line no 1
line no 2
line no 3

在不更改读取的主要方法的情况下

static void Main(string[] args)
{
    Console.WriteLine("line no 2");
}

现在一种方法是为控制台应用程序设置多个入口点。但是我尝试了另一种方法,如下所示:

class Program
{
    [Some]
    static void Main(string[] args)
    {
        Console.WriteLine("line no 2");
    }
}
class SomeAttribute : Attribute
{
    public SomeAttribute()
    {
        Console.WriteLine("line no 1");
    }
    ~SomeAttribute()
    {
        Console.WriteLine("line no 3");
    }
}

当我在每个WriteLine上应用断点时,我可以看到该方法有效,但是,控制台上没有反映出来。

只是好奇。

4

3 回答 3

15

您的问题可以分解为钩子的搜索,这些钩子Main在控制台应用程序的方法执行之前和之后触发。

  • 第一个钩子是一个Program静态构造函数,它保证在类中的方法之前 执行。MainProgram

  • 其次是 a 的 ProcessExit 事件 ,AppDomain在默认应用程序域的父进程退出时发生”。您可以使用静态构造函数来订阅此事件。


class Program
{
    static Program()
    {
        Console.WriteLine("line no 1");

        AppDomain.CurrentDomain.ProcessExit += 
                                          (s, a) => Console.WriteLine("line no 3");
    }

    static void Main(string[] args)
    {
        Console.WriteLine("line no 2");
    }
}

印刷:

line no 1
line no 2
line no 3

下一部分会很长。我将尝试解释SomeAttribute您的问题有什么问题。

首先,考虑这个 StackOverflow 问题,以准确了解自定义属性构造函数何时执行。这并不像乍一看那么简单。

正如我们已经知道的,自定义属性的 ctor 只有在您通过反射访问它时才会被执行。因此,在您的示例中,简单的程序执行不会触发属性构造函数。SomeAttribute但是,当您应用于方法时,为什么您的断点会命中Main?事实证明,Visual Studio 使用反射来找出主要方法并将调试器附加到您的应用程序。但此时没有控制台窗口。所以声明Console.WriteLine是无用的,产生效果。此外,它似乎阻止了控制台输出的所有下一条语句。

所以接下来的代码会产生不同的结果,这取决于你是否使用 VS 调试器运行它:

class Program
{
    [MyAttribute]
    static void Main()
    {

    }
}

class MyAttribute : Attribute
{
    public MyAttribute()
    {
        MessageBox.Show("MyAttribute ctor");
    } 
}

如果您在没有调试器的情况下运行它(在 VS 默认配置中为Ctrl + F5),您会看到,该程序终止并且没有出现任何窗口。当您使用调试器(F5)执行它时,您会看到

在此处输入图像描述

VS 旁边没有控制台窗口,只有 win 表单图标: 在此处输入图像描述

正如我之前所描述的,当您在没有人的情况下尝试写入控制台时,所有其他调用Console.WriteLine都不会影响您的控制台应用程序。这就是为什么您可以看到任何控制台消息,即使您在构造函数中执行断点也是如此。

于 2013-04-13T11:08:23.470 回答
6

我认为Ilya Ivanov 的答案可能是最好的答案。但是,我的答案也很有趣:

public class Program
{
    static Program()
    {
        Console.WriteLine("line no 1");
        Console.WriteLine("line no 2");
        Console.WriteLine("line no 3");
        Environment.Exit(0);
    }

    static void Main(string[] args)
    {
        Console.WriteLine("line no 2");
    }
}
于 2013-04-13T11:17:57.857 回答
4

让我们使用 AOP,并利用 PostSharp。您需要先下载并安装它,然后您需要使用 NuGet 添加对它的引用。您必须安装它,因为它是编译器的挂钩。看,当你编译你的代码时,PostSharp 实际上会根据你使用的钩子将 IL 注入到输出中。

完成这两件事后,为 AOP 属性添加一个新类:

using PostSharp.Aspects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    [Serializable]
    public class ConsoleAspect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionArgs args)
        {
            base.OnEntry(args);

            Console.WriteLine("line no 1");
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            base.OnExit(args);

            Console.WriteLine("line no 3");
        }
    }
}

然后Main像这样修改你的方法:

[ConsoleAspect]
static void Main(string[] args)
{
    Console.WriteLine("line no 2");
}
于 2013-04-13T11:06:36.620 回答