C# 如何避免仅用于依赖项注入的未使用字段?

C# 如何避免仅用于依赖项注入的未使用字段?,c#,C#,好吧,从技术上讲,它们不是“未使用的”,但它们从未被持有它们的类使用过,所以我猜在某种意义上它们是。这是一个假设的情况,我正试图找到答案 我有一个类NetworkListener public NetworkListener(IPAddress ipAddress, int port, NetworkClientPacketRepository packetRepository) NetworkListener有时必须创建一个新类,该类位于合成根目录之外: new NetworkClient(

好吧,从技术上讲,它们不是“未使用的”,但它们从未被持有它们的类使用过,所以我猜在某种意义上它们是。这是一个假设的情况,我正试图找到答案

我有一个类
NetworkListener

public NetworkListener(IPAddress ipAddress, int port, NetworkClientPacketRepository packetRepository)
NetworkListener有时必须创建一个新类,该类位于合成根目录之外:

new NetworkClient(await _listener.AcceptTcpClientAsync(), _packetRepository, _logger);
services.AddSingleton<NetworkEventArguments>();
services.AddSingleton<NetworkClientRepository>();
services.AddSingleton<Dictionary<int, IClientPacket>>();
services.AddSingleton<ClientPacketRepository>();
services.AddSingleton(provider => new NetworkListener(
    IPAddress.Parse(config.GetValue<string>("Networking:Host")),
    config.GetValue<int>("Networking:Port"),
    provider.GetService<NetworkClientRepository>()
));
这是坏习惯吗?感觉是这样,看起来是这样,所以一定是这样,对吧?我不确定

有几件事对我来说很突出,表明它是

A.NetworkListener必须接受
NetworkClientRepository
依赖项,因为
NetworkClient
需要它

B.NetworkListener必须接受
记录器
依赖项,因为
NetworkClient
需要它

这似乎是错误的,但我不知道更好的方法,因此我为什么这样做

如何避免将未使用的字段传递到子类

这是我的作文根:

new NetworkClient(await _listener.AcceptTcpClientAsync(), _packetRepository, _logger);
services.AddSingleton<NetworkEventArguments>();
services.AddSingleton<NetworkClientRepository>();
services.AddSingleton<Dictionary<int, IClientPacket>>();
services.AddSingleton<ClientPacketRepository>();
services.AddSingleton(provider => new NetworkListener(
    IPAddress.Parse(config.GetValue<string>("Networking:Host")),
    config.GetValue<int>("Networking:Port"),
    provider.GetService<NetworkClientRepository>()
));
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton(provider=>newNetworkListener(
IPAddress.Parse(config.GetValue(“Networking:Host”),
config.GetValue(“网络:端口”),
provider.GetService()
));
NetworkListener类:

public class NetworkListener : IDisposable
{
    private readonly NetworkClientRepository _clientRepository;
    private readonly ClientPacketRepository _packetRepository;
    private readonly TcpListener _listener;

    public NetworkListener(IPAddress ipAddress, int port, NetworkClientRepository clientRepository, ClientPacketRepository packetRepository)
    {
        _clientRepository = clientRepository;
        _packetRepository = packetRepository;
        _listener = new TcpListener(ipAddress, port);
    }

    public void Start(int backlog = 100)
    {
        _listener.Start(backlog);
    }

    public async Task ListenAsync()
    {
        while (true)
        {
            HandleIncomingConnection(await _listener.AcceptTcpClientAsync());
        }
    }
    
    public event EventHandler<NetworkEventArguments> ClientConnected;
    
    private void HandleIncomingConnection(TcpClient client)
    {
        var networkClient = new NetworkClient(client, _packetRepository);
        
        _clientRepository.AddClient(networkClient);
        
        ClientConnected?.Invoke(this, new NetworkEventArguments
        {
            Client = networkClient
        });
    }

    public void Dispose()
    {
        _listener.Server.Close();
        _listener.Server.Dispose();
    }
}
公共类NetworkListener:IDisposable
{
专用只读网络clientRepository\u clientRepository;
专用只读客户端packetRepository _packetRepository;
专用只读TcpListener\u侦听器;
公用网络侦听器(IPAddress IPAddress、int端口、NetworkClientRepository clientRepository、ClientPacketRepository packetRepository)
{
_clientRepository=clientRepository;
_packetRepository=packetRepository;
_侦听器=新的TcpListener(IP地址,端口);
}
公共void开始(int backlog=100)
{
_listener.Start(backlog);
}
公共异步任务ListenAsync()
{
while(true)
{
HandleIncomingConnection(wait_listener.AcceptCpclientAsync());
}
}
公共事件事件处理程序ClientConnected;
私有无效HandleIncomingConnection(TcpClient客户端)
{
var networkClient=新的networkClient(客户端,_packetRepository);
_clientRepository.AddClient(networkClient);
ClientConnected?.Invoke(此,新的NetworkEventArguments)
{
客户端=网络客户端
});
}
公共空间处置()
{
_listener.Server.Close();
_Dispose();
}
}
NetworkClient类:

public class NetworkClient : IDisposable
{
    public TcpClient TcpClient { get; }
    
    private readonly ClientPacketRepository _packetRepository;
    private readonly ILogger _logger;
    private readonly NetworkStream _networkStream;

    public NetworkClient(TcpClient tcpClient, ClientPacketRepository packetRepository, ILogger logger)
    {
        TcpClient = tcpClient;
        
        _packetRepository = packetRepository;
        _logger = logger.ForContext<NetworkClient>();
        _networkStream = tcpClient.GetStream();

        ProcessDataAsync();
    }
    
    private void ProcessDataAsync()
    {
        var thread = new Thread(() =>
        {
            // TODO: Read from tcpClient
        });
        
        thread.Start();
    }

    public void Dispose()
    {
        TcpClient.Dispose();
    }
}
公共类网络客户端:IDisposable
{
公共TcpClient TcpClient{get;}
专用只读客户端packetRepository _packetRepository;
专用只读ILogger\u记录器;
专用只读网络流_NetworkStream;
公用网络客户端(TcpClient TcpClient、ClientPacketRepository packetRepository、ILogger记录器)
{
TcpClient=TcpClient;
_packetRepository=packetRepository;
_logger=logger.ForContext();
_networkStream=tcpClient.GetStream();
ProcessDataAsync();
}
私有void ProcessDataAsync()
{
变量线程=新线程(()=>
{
//TODO:从tcpClient读取
});
thread.Start();
}
公共空间处置()
{
TcpClient.Dispose();
}
}
这是坏习惯吗?感觉是这样,看起来是这样,所以一定是这样,对吧?我不确定

当然是这样。您的类应该只实例化不能注入的数据类或第三方服务。实例化
TcpListener
就是后者的一个很好的例子

你的问题来自这里:

public NetworkClient(TcpClient tcpClient, ClientPacketRepository packetRepository, ILogger logger)
尽量避免在构造函数中将依赖项和“数据”混合在一起。相反,让构造函数只有可以注入的参数,并将其余参数放入适当的方法中

以下是
NetworkClient
的外观:

public interface INetworkClient 
{
    void ProcessDataAsync(TcpClient tcpClient);
}

public class NetworkClient : INetworkClient, IDisposable
{
    private readonly ClientPacketRepository _packetRepository;
    private readonly ILogger _logger;

    public NetworkClient(ClientPacketRepository packetRepository, ILogger logger)
    {
        _packetRepository = packetRepository;
        _logger = logger.ForContext<NetworkClient>();
    }
    
    // this Async in the name and the Thread being created internally looks smelly
    private void ProcessDataAsync(TcpClient tcpClient)
    {
        var networkStream = tcpClient.GetStream();
        // ...
    }

    // ...
}
因此,您可以将其用于:

private void HandleIncomingConnection(TcpClient client)
{
    var networkClient = _networkClientFactory.Create();
    netowrkClient.ProcessDataAsync(client);
    
    _clientRepository.AddClient(networkClient);
    
    ClientConnected?.Invoke(this, new NetworkEventArguments
    {
        Client = networkClient
    });
}

NetworkClient
对象何时创建?在构造函数期间或以后?它是在TcpListener有新的传入连接时创建的。@CamiloterienTo我使用的是
Microsoft.Extensions.DependencyInjection
,但是
NetworkClient
是一个临时的,在TcpListener有新连接时创建
NetworkListener
在我的DI容器中。@Camilotervento我已经意识到,我的问题是如何避免这样做。你能编写一个
NetworkClientFactory
来获取一个新的
NetworkClient
对象吗?您可以注入此实例(已解决所有依赖项),因此不再需要
ILogger
NetworkClientRepository
引用。这似乎是在传播问题,而不是解决问题,工厂仍然需要以某种方式从DI容器中获取记录器实例。不是真的,工厂只需要从迪肯那里得到一个暂时的例子,你的答案中包括一个这样的例子吗?否则,我将陷入与以前相同的境地,不知道如何在类中实例化类,以及如何获取该类的依赖项,而不将它们存储在创建新类的类中。我正在使用Serilog。编辑了答案@aaabe,因为无法将来自
\u listener.AcceptTcpClientAsync()
的TcpClient传入
NetworkClient
的构造函数。我认为构建它就像
返回新的NetworkClient(client,_serviceProvider.GetService(),_serviceProvider.GetService())
是唯一的选项,或者使用ActivatorUtilities获得更少的代码,但方法相同。