我正在使用Visual Studio 2017。
每当我按F5
开始调试程序时,我都会注意到Main(string[] args)
类中的Program
方法没有被调用,即使Program
中的字段被调用了也是如此。如下面的屏幕截图所示,已初始化:
在创建一个TcpClient
实例并将其分配给相应的字段之后,调试器将永远不会达到我在Main(string[] args)
方法上设置的断点。
可以肯定的是,我已将项目的启动对象设置为Program
类。那不能解决问题:
我想念什么?
编辑:
我在Console.WriteLine("Entering Main method...")
方法中添加了Main
,但是当我开始调试时,它不会打印到控制台上。
在创建TcpClient
实例之后,实际上什么也没有发生(或者,没有立即可见的东西)-没有抛出异常;该程序不会自行终止;控制台保持空白。
编辑:
证明TcpClient
构造函数内部发生崩溃。
答案 0 :(得分:5)
请记住,TcpClient(string, int)
构造函数此时会打开一个新连接(doc):
初始化TcpClient类的新实例,并连接到指定主机上的指定端口。
...
该构造函数创建一个新的TcpClient并尝试对提供的主机名和端口号进行同步连接。
如果我复制/粘贴您的代码(插入我自己的RemoteServerIpAddressString
),那么在尝试构建TcpClient
时,应用程序会挂起。如果此时中断调试器,则可以看到它卡在了System.Net.Sockets.Socket.DoConnect
中,后者正在尝试连接到远程计算机。
一段时间后它放弃,抛出异常,并抛出TypeInitializationException
,这会破坏调试器。
这与您的观察相符:
从字面上看,在创建TcpClient实例之后,什么也没有发生(或者,没有立即可见的东西)-没有抛出异常;该程序不会自行终止;控制台保持空白。
这时,TcpClient
仍在尝试连接。在成功之前,永远不会初始化类型,并且在这种情况发生之前,Main
将永远不会运行。如果您将其放置足够长时间,则可能会像我一样失败。
如果我确定TcpClient
正在连接到开放的端口,则TcpClient
构造函数将立即完成并运行Main
。
在静态构造函数中执行长时间运行的操作(尤其是与网络有关的操作)是一个非常糟糕的主意。在初始化类型时,CLR需要获取一个锁,这将阻止其他类型的初始化,并可能导致死锁。
您可能想在TcpClient
方法内部构造Main
,或将其构造为:
private static readonly TcpClient TcpClient = new TcpClient();
然后在主目录中:
TcpClient.Connect(...);
答案 1 :(得分:1)
静态字段初始化程序(程序类)不应包含可能抛出或超时的代码。
问题中突出显示的代码是静态字段初始化程序。这将在第一次访问类型before any static method or even the static constructor时运行。如果初始化程序或静态构造函数阻塞或抛出,则应用程序将终止而不调用Main
。这意味着不能使用任何错误处理代码来捕获那些异常。
这种有保证的顺序使得在C#中非常简单地实现简单的单例。不需要双重锁定,因为可以保证执行顺序。查看乔恩·斯基特(Jon Skeet)在Singleton implementation上的文章:
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;
}
}
}
足以创建一个线程安全的单例