使用TcpListener的简单类(这只是为了解决问题,绝不会让这个类有任何实际意义):
using System;
using System.Net.Sockets;
namespace NUnitTcp
{
public class Foo
{
TcpListener lst;
public Foo()
{
lst = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 9090);
}
~Foo()
{
lst.Stop();
}
public void Start()
{
lst.Start();
}
public void Stop()
{
lst.Stop();
}
}
}
使用Foo的简单应用程序:
using System;
namespace NUnitTcp
{
class Program
{
static void Main(string[] args)
{
Foo f = new Foo();
f.Start();
}
}
}
此应用程序运行正常,应用程序结束后即可释放端口,并且应用程序可以再次运行!即使没有Foo中的析构函数,这仍然会发生!
使用Foo进行简单的NUnit测试:
using System;
using NUnitTcp;
using NUnit.Framework;
namespace NUnitTcpTests
{
[TestFixture]
public class TcpTests
{
[Test]
public void test1()
{
Foo f = new Foo();
f.Start();
Assert.True(true);
}
}
}
此测试将在NUnit GUI中运行一次。该测试的任何后续执行都将抛出异常,通知我们该端口正在使用中。 NUnit GUI重启将释放端口 你认为这是一个错误吗?对我来说就像一个......
更正 - 测试将随机抛出异常,对我来说大约有70%的时间。
答案 0 :(得分:5)
垃圾收集器是不确定的。它仅在第一个示例中立即关闭,因为该进程退出。我强烈建议您实施IDisposable
而不是使用终结器,然后您可以使用:
using(Foo f = new Foo())
{
f.Start();
Assert.True(true);
}
安全地知道它会立即关闭。
有类似的东西:
public void Dispose()
{
if(lst != null) lst.Stop();
lst = null;
}
答案 1 :(得分:2)
端口正在使用,直到垃圾收集器不会收集您的Foo
实例。因此你定义了终结器,它将需要两个垃圾收集来调用终结器(Foo
将在第一次垃圾收集期间移动到终结队列,终结器将在第二次调用期间可能垃圾收集)。如果您想确保端口将被释放,我建议您在Foo
方法中手动关闭停止TearDown
:
private Foo _foo;
[SetUp]
public void Setup()
{
_foo = new Foo();;
}
[Test]
public void test1()
{
_foo.Start();
// Assert
}
[TearDown]
public void TearDown()
{
if (_foo != null)
_foo.Stop();
}
同样在IDisposable
类上实现Foo
会很好,因为它使用了非托管资源,应该将其释放:
public class Foo : IDisposable
{
TcpListener lst;
public Foo()
{
lst = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 9090);
}
public void Dispose()
{
lst.Stop();
}
}