简体   繁体   English

未调用 Main(string[] args) 方法

[英]Main(string[] args) method not called

I am using Visual Studio 2017.我正在使用 Visual Studio 2017。

Whenever I press F5 to start debugging my program, I notice that the Main(string[] args) method inside the Program class is not called, even though the fields inside Program are initialized, as you can see from the screenshot below:每当我按F5开始调试我的程序时,我注意到Program class 中的Main(string[] args)方法没有被调用,即使Program中的字段已初始化,如下面的屏幕截图所示:

在此处输入图像描述

After creating a TcpClient instance and then assigning it to the corresponding field, the debugger never hits the breakpoint I have set on the Main(string[] args) method.创建TcpClient实例并将其分配给相应的字段后,调试器永远不会命中我在Main(string[] args)方法上设置的断点。

Just to be sure, I have set the startup object for my project to be the Program class. That did not fix the issue:可以肯定的是,我已将项目的启动 object 设置为Program class。这并没有解决问题:

在此处输入图像描述

What am I missing?我错过了什么?

EDIT:编辑:

I have added Console.WriteLine("Entering Main method...") inside my Main method, but it is not printed to the console when I start debugging.我在我的Main方法中添加了Console.WriteLine("Entering Main method...") ,但是当我开始调试时它没有打印到控制台。

Literally nothing (or rather, nothing immediately visible) happens after the TcpClient instance is created -- no exceptions thrown;在创建TcpClient实例之后,几乎没有任何事情(或者更确切地说,没有立即可见的事情)发生——没有抛出异常; the program doesn't self-terminate;该程序不会自行终止; the console stays blank.控制台保持空白。

EDIT:编辑:

Turns out a crash is occurring inside the TcpClient constructor.结果是TcpClient构造函数内部发生了崩溃。

Remember that the TcpClient(string, int) constructor opens a new connection, at that point ( doc ): 请记住,此时TcpClient(string, int)构造函数将打开一个新连接( doc ):

Initializes a new instance of the TcpClient class and connects to the specified port on the specified host. 初始化TcpClient类的新实例,并连接到指定主机上的指定端口。
... ...
This constructor creates a new TcpClient and makes a synchronous connection attempt to the provided host name and port number. 此构造函数创建一个新的TcpClient并尝试对提供的主机名和端口号进行同步连接。

If I copy/paste your code (inserting my own RemoteServerIpAddressString ), then I see the application hang as it tries to construct the TcpClient . 如果我复制/粘贴您的代码(插入我自己的RemoteServerIpAddressString ),那么在尝试构造TcpClient ,应用程序会挂起。 If I break the debugger at that point, I can see that it's stuck in System.Net.Sockets.Socket.DoConnect , which is trying to connect to the remote machine. 如果我在那时中断了调试器,则可以看到它卡在了System.Net.Sockets.Socket.DoConnect ,后者正在尝试连接到远程计算机。

After a while it gives up, throws an exception, and a TypeInitializationException gets thrown, which breaks the debugger. 一段时间后,它会放弃并引发异常,并引发TypeInitializationException ,这将破坏调试器。

This matches your observation: 这符合您的观察:

Literally nothing (or rather, nothing immediately visible) happens after the TcpClient instance is created -- no exceptions thrown; 从字面上看,在创建TcpClient实例之后,什么也没有发生(或者,没有立即可见的东西)。 the program doesn't self-terminate; 该程序不会自行终止; the console stays blank. 控制台保持空白。

At this point, the TcpClient is still trying to connect. 此时, TcpClient仍在尝试连接。 Until it succeeds, the type never gets initialized, and until this happens Main will never be run. 在成功之前,永远不会初始化类型,并且在这种情况下, Main永远不会运行。 If you leave it long enough it will probably fail, just as mine did. 如果您将其放置足够长的时间,它可能会像我的一样失败。

If I make sure that the TcpClient is connecting to a port which is open, then the TcpClient constructor completes straight away and Main runs. 如果我确定TcpClient正在连接到打开的端口,则TcpClient构造函数将立即完成并运行Main


It's a very bad idea to do long-running things - especially network-y things - inside a static constructor. 在静态构造函数中执行长时间运行的操作(尤其是与网络有关的操作)是一个非常糟糕的主意。 The CLR needs to acquire a lock when a type is being initialized, and this stops other types from being initialized, and can cause deadlocks. 在初始化类型时,CLR需要获取一个锁,这将阻止其他类型的初始化,并可能导致死锁。

You probably want to either construct the TcpClient inside your Main method, or construct it as: 您可能想要在Main方法中构造TcpClient ,或将其构造为:

private static readonly TcpClient TcpClient = new TcpClient();

and then in main: 然后在主要:

TcpClient.Connect(...);

Static field initializers in this case (the Program class) shouldn't contain code that may throw or time out. 在这种情况下,静态字段初始化程序(Program类)不应包含可能抛出或超时的代码。

The code highlited in the question is a static field initializer. 问题中突出显示的代码是静态字段初始化程序。 This will run the first time a type is accessed, before any static method or even the static constructor . 这将在任何静态方法甚至静态构造函数之前首次访问类型时运行。 If the initializers or the static constructor block or throw, the application will terminated without calling Main . 如果初始化程序或静态构造函数阻塞或抛出,则应用程序将终止而不调用Main This means that no error handling code can be used to catch those exceptions. 这意味着不能使用任何错误处理代码来捕获那些异常。

This guaranteed order makes implementing simple singletons in C# very easy. 这种有保证的顺序使在C#中实现简单的单例变得非常容易。 No double locking is needed as the order of execution is guaranteed. 不需要双重锁定,因为可以保证执行顺序。 Check Jon Skeet's article on Singleton implementation : 查看Jon Skeet的有关Singleton实现的文章:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()    {    }
    private Singleton()   {    }

    public static Singleton Instance
    {
        get 
        {
            return instance;
        }
    }
}

That's enough to create a thread-safe singleton 这足以创建线程安全的单例

I'm adding a new answer because I hit a similar issue in a more complex hosting environment that was hiding class load and other initialization exceptions.我正在添加一个新的答案,因为我在隐藏 class 负载和其他初始化异常的更复杂的托管环境中遇到了类似的问题。

In order to catch the issue, I did this:为了解决这个问题,我这样做了:

  • Renamed my Main method to Main2 , so it does not conflict with the next change.将我的Main方法重命名为Main2 ,因此它不会与下一个更改冲突。
  • Created another Program class, clean this time, that calls Main2 , like the one below:创建了另一个程序 class,这次清理,它调用Main2 ,如下所示:
class CleanProgram {

    static void Main(string[] args) {
        try {
            Program.Main2(args);
        } catch (Exception ex) {
            Console.WriteLine("{0}", ex);
        }
    }

}

The change above is only for diagnostics purposes!上面的更改仅用于诊断目的! After you find and fix the problem, the change can be undone.找到并解决问题后,可以撤消更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM