gpt4 book ai didi

C# 依赖容器和构造函数

转载 作者:太空狗 更新时间:2023-10-30 00:39:35 24 4
gpt4 key购买 nike

我花了一些时间记录自己的依赖注入(inject)和 IoC,但我还没有找到解决我的问题的方法。

我的问题与使用依赖容器时对象的实例化有关,因为它创建了对构造函数参数的依赖。在我遇到的几乎每个示例中,具体类的构造函数都没有任何参数。它使一切变得相当“简单”。因此我的问题在这里。

举个例子:我需要从两个来源A和B下载一些数据。来源A包含各种格式的数据;例如 csv 和 xml。我们不需要为源 B 指定这样的东西。

这是一些代码(请注意,我尽可能地简化了代码以说明我的观点):

using System.Net;
using System.IO;
using System.Reflection;

namespace Question
{
class Program
{
static void Main(string[] args)
{
//exemple of code using Client A
DependencyContainer container1 = GetContainer1();
IClient client1 = container1.Resolve<IClient>("xml");
User user1 = new User(client1);
user1.run();

DependencyContainer container2 = GetContainer2();
IClient client2 = container2.Resolve<IClient>();
User user2 = new User(client2);
user2.run();
}

public static DependencyContainer GetContainer1()
{
DependencyContainer container = new DependencyContainer();
container.Register<IClient, ClientA>();
return container;
}

public static DependencyContainer GetContainer2()
{
DependencyContainer container = new DependencyContainer();
container.Register<IClient, ClientB>();
return container;
}
}

public class User
{
private readonly IClient _Client;

public User(IClient client)
{
_Client = client;
}

public void run()
{
string address = _Client.getAddress();
string data = _Client.getData(address);
_Client.writeData(data);
}
}
// Abstraction
public interface IClient
{
/// <summary>
/// create the address or the name of the file storing the data
/// </summary>
string getAddress();

/// <summary>
/// uses a WebClient to go and get the data at the address indicated
/// </summary>
string getData(string adress);

/// <summary>
/// Write the data in a local folder
/// </summary>
void writeData(string data);
}

//Implementation A
public class ClientA : IClient
{
// Specify the type of the file to be queried in the database
// could be a csv or an xml for example
private readonly string _FileType;

public ClientA(string fileType)
{
_FileType = fileType;
}

public string getAddress()
{
return "addressOfFileContainingData." + _FileType;
}

public string getData(string address)
{
string data = string.Empty;
using (WebClient client = new WebClient())
{
data = client.DownloadString(address);
}
return data;
}

public void writeData(string data)
{
string localAddress = "C:/Temp/";
using (StreamWriter writer = new StreamWriter(localAddress))
{
writer.Write(data);
}
}
}

//Implementation B
public class ClientB : IClient
{
public ClientB()
{
}

public string getAddress()
{
return "addressOfFileContainingData";
}

public string getData(string address)
{
string data = string.Empty;
using (WebClient client = new WebClient())
{
data = client.DownloadString(address);
}
return data;
}

public void writeData(string data)
{
string localAddress = "C:/Temp/";
using (StreamWriter writer = new StreamWriter(localAddress))
{
writer.Write(data);
}
}
}

public class DependencyContainer
{
private Dictionary<Type, Type> _Map = new Dictionary<Type, Type>();

public void Register<TypeToResolve, ResolvedType>()
{
_Map.Add(typeof(TypeToResolve), typeof(ResolvedType));
}

public T Resolve<T>(params object[] constructorParameters)
{
return (T)Resolve(typeof(T), constructorParameters);
}

public object Resolve(Type typeToResolve, params object[] constructorParameters)
{
Type resolvedType = _Map[typeToResolve];
ConstructorInfo ctorInfo = resolvedType.GetConstructors().First();
object retObject = ctorInfo.Invoke(constructorParameters);
return retObject;
}
}

我倾向于认为这段代码中有一些好的地方,但请随时纠正我。然而,实例化:

IClient client = container.Resolve<IClient>("xml");

IClient client = container.Resolve<IClient>();

给我带来了很多担忧。高级模块(此处为 User 类)并不像预期的那样依赖于具体实现。但是,现在类 Program 依赖于具体类的构造函数的结构!因此,它通过在其他地方制造更大的问题来解决一个问题。我宁愿依赖于具体的实现,而不是依赖于它的构造函数的结构。假设ClientA的代码被重构,构造函数被改变,那么我不知道类Program实际上使用它。

最后,我的问题:

  1. 我是否错过了 IoC 的要点?
  2. 我是不是用错了?
  3. 如果没有,如何解决这个问题?

一种解决方案是在 ClientA 的构造函数中不包含任何参数。但这是否意味着构造函数在使用依赖容器时不应该有任何参数?或者这是否意味着在其构造函数中带有参数的对象不适合这种技术?还可以争辩说,ClientA 和 ClientB 不应派生自同一个接口(interface),因为它们本质上的行为方式不同。

感谢您的评论和意见。

最佳答案

have I missed the point of IoC?

是也不是。幸运的是,你的具体类(UserClientAClientB)都依赖于Constructor Injection,这是最重要的依赖注入(inject) (DI) 模式。另一方面,DI 容器完全是可选的。

因此,使用 Pure DI ,您只需像这样实现您的 Main 方法:

static void Main(string[] args)
{
//exemple of code using Client A
User user1 =
new User(
new ClientA(
"xml"));
user1.run();

User user2 =
new User(
new ClientB());
user2.run();
}

这样不仅方便大家理解,还给你compile-time feedback当您编写对象图时。

DI 最重要的目标是确保实现代码适当解耦,这正是构造函数注入(inject)的帮助。

Am I miss-using it?

也许有一点,但不多。如果你想使用 DI 容器而不是纯 DI,你应该遵循 Register Resolve Release pattern .如果你想要一个 User 对象,你应该请求它,而不是请求一个 IClient 对象:

var user = container.Resolve<User>();
user.run();

在容器中适本地注册所有服务取决于您。如果你想使用 ClientA,你需要告诉容器它应该为 fileType 构造函数参数使用哪个值。具体如何操作取决于您使用的特定 DI 容器。

但是,有时您可以为 primitive dependencies 定义约定,例如 pulling all primitive values from the application's configuration file .

If not, how to solve this issue?

我的建议是使用上述纯 DI 方法,除非您有 compelling reason to use a DI Container .根据我的经验,这种情况很少发生。

关于C# 依赖容器和构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34464597/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com