C# 使用控制反转范例创建自定义对象列表

C# 使用控制反转范例创建自定义对象列表,c#,unit-testing,dependency-injection,inversion-of-control,abstraction,C#,Unit Testing,Dependency Injection,Inversion Of Control,Abstraction,我想将依赖注入和控制反转适应到我的日常开发中。假设我有一个对象类型SomeObject(实现接口isomoobject)。我有一个类,它使用这个名为Data的对象,它实现了IData接口 public interface ISomeObject { int ID; string Name; bool IsAwesome; void DoSomeStuffIfAwesome(); } public Class SomeObject : ISomeObject {

我想将依赖注入和控制反转适应到我的日常开发中。假设我有一个对象类型
SomeObject
(实现接口
isomoobject
)。我有一个类,它使用这个名为Data的对象,它实现了
IData
接口

public interface ISomeObject {
    int ID;
    string Name;
    bool IsAwesome;

    void DoSomeStuffIfAwesome();
}

public Class SomeObject : ISomeObject {
    int ID;
    string Name;
    bool IsAwesome;

    void DoSomeStuffIfAwesome() { /*stuff happens here*/ }        
}

public interface IData {
    List<ISomeObject> GetSomeObjects();
}

public Class Data : IData {
    List<ISomeObject> GetSomeObjects()
    {
        List<ISomeObject> objects = new List<ISomeObject>; // ??? Maybe and cast later?

        //do some SQL stuff and get a SqlDataReader object called reader
        while(reader.Read()) {
             //ISomeObject someObj = ???
             //Read into the someObj.ID, someObj.Name and someObj.IsAwesome fields
             objects.add(someObj);
        }
        return objects;
    }
}    
公共接口对象{
int-ID;
字符串名;
布尔·伊萨维索姆;
void DoSomeStuffIfAwesome();
}
公共类SomeObject:ISomeObject{
int-ID;
字符串名;
布尔·伊萨维索姆;
void DoSomeStuffIfAwesome(){/*这里发生的事情*/}
}
公共接口IData{
列出GetSomeObjects();
}
公共类数据:IData{
列出GetSomeObjects()
{
列表对象=新列表;/??可能以后再强制转换?
//执行一些SQL操作并获得一个名为reader的SqlDataReader对象
while(reader.Read()){
//ISomeObject someObj=???
//读入someObj.ID、someObj.Name和someObj.IsAwesome字段
添加(someObj);
}
归还物品;
}
}    
GetSomeObjects()
方法生成
isomoObject
对象的列表。但是我不希望
Data.cs
将任何与
SomeObject
相关的内容硬编码到它中。我需要某种形式的依赖注入来解决运行时的问题。最好的处理方法是什么?我考虑了以下几点:

1。将
SomeObject
的实例传递到
Data
的构造函数中。
这样,我可以使用
.GetType()
获取其类型,将其存储到私有
系统中。在
Data.cs
中键入
变量,并在循环中使用
Activator.CreateInstance
创建要添加到列表中的新对象<如果我理解正确,代码>数据
需要知道专门用于强制转换的
SomeObject

2。将我的IoC容器的一个实例传递给
数据
的构造函数,只需使用
container.resolve()
解析对象类型即可。如果不使用IoC容器,这将使单元测试
GetSomeObjects()
方法变得困难。我已经读到我不应该在单元测试期间使用IoC容器,应该手动将我需要的传递到方法中


3。传递一个已被实例化为
SomeObject
isomoobject
对象-然后我将使用该对象通过一些内置方法创建对象,例如
SomeObject.GenerateList(IDataReader reader)

您可以将对象的创建委托给其他对象

public interface ISomeObjectFactory {
    ISomeObject Create(IDataReader reader);
}
它只负责创建
ISomeObject

using System.Collections.Generic;
using System.Data;

public interface IDbConnectionFactory {
    ///<summary>
    ///  Creates a connection based on the given database name or connection string.
    ///</summary>
    IDbConnection CreateConnection(string nameOrConnectionString);
}

public class Data : IData {
    private IDbConnectionFactory dbConnectionFactory;
    ISomeObjectFactory someObjectFactory;
    private string CONNECTION_STRING = "Connection string here";

    public Data(IDbConnectionFactory dbConnectionFactory, ISomeObjectFactory objectFactory) {
        this.dbConnectionFactory = dbConnectionFactory;
        this.someObjectFactory = objectFactory;
    }

    public List<ISomeObject> GetSomeObjects() {
        var objects = new List<ISomeObject>();
        //do some SQL stuff and return a data reader
        using (var connnection = dbConnectionFactory.CreateConnection(CONNECTION_STRING)) {
            using (var command = connnection.CreateCommand()) {
                //configure command to be executed.
                command.CommandText = "SELECT * FROM SOMEOBJECT_TABLE";
                connnection.Open();
                using (var reader = command.ExecuteReader()) {
                    while (reader.Read()) {
                        //...Logic to populate item
                        var someObject = someObjectFactory.Create(reader);
                        if (someObject != null)
                            objects.Add(someObject);
                    }
                }
            }
        }

        return objects;
    }
}
使用System.Collections.Generic;
使用系统数据;
公共接口IDbConnectionFactory{
///
///基于给定的数据库名称或连接字符串创建连接。
///
IDbConnection CreateConnection(字符串名称或连接字符串);
}
公共类数据:IData{
私有IDbConnectionFactory dbConnectionFactory;
ISomeObjectFactory someObjectFactory;
私有字符串连接\u string=“此处的连接字符串”;
公共数据(IDbConnectionFactory dbConnectionFactory、ISomeObjectFactory objectFactory){
this.dbConnectionFactory=dbConnectionFactory;
this.someObjectFactory=objectFactory;
}
公共列表GetSomeObjects(){
var objects=新列表();
//执行一些SQL操作并返回一个数据读取器
使用(var connnection=dbConnectionFactory.CreateConnection(连接\字符串)){
使用(var command=connnection.CreateCommand()){
//配置要执行的命令。
command.CommandText=“从SOMEOBJECT_表中选择*”;
connection.Open();
使用(var reader=command.ExecuteReader()){
while(reader.Read()){
//…填充项的逻辑
var someObject=someObjectFactory.Create(reader);
if(someObject!=null)
对象。添加(someObject);
}
}
}
}
归还物品;
}
}

这样,数据只依赖于抽象,而不依赖于具体。这些可以在运行时在合成根目录中确定/配置。

在某些情况下,类必须是具体的。为什么
IData
的实现不知道如何返回一组特定的
isomoObject
对象呢?很可能就是这种情况。我的理解是,IoC和依赖注入的目标是让使用者只知道接口,而不知道具体类,这样我就可以快速交换具体类,而无需修改使用者。因此,如果将来我想用另一个实现ISomeObject的类来替换某个对象,我可以不用碰Data.cs。这很可能是因为我对抽象的概念研究得太深入了,需要对它进行一些调整。也许我确实需要实例化Data.cs中的某个对象。请使用一个工厂(
isomobjectfactory
),它接收读取器并返回一个
isomobject
)。将工厂注入数据,工厂将知道如何构造
isomobject
。这个答案很好,应该可以做到!非常感谢