关于c#:使用IoC容器时原始构造函数参数是个坏主意吗?

Are primitive constructor parameters a bad idea when using an IoC Container?

标准的新手免责声明:我是国际奥委会的新手,收到的信号不一。我在找以下情况的指导。

假设我有以下接口和实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public interface IImageFileGenerator
{
    void RenameFiles();
    void CopyFiles();
}

public class ImageFileGenerator : IImageFileGenerator
{
    private readonly IList<IImageLink> _links;
    private readonly string _sourceFolder;
    private readonly string _destinationFolder;
    private readonly int _folderPrefixLength;

    public ImageFileGenerator(IList<IImageLink> links, string sourceFolder, string destinationFolder)
    {
        _links = links;
        _sourceFolder = sourceFolder;
        _destinationFolder = destinationFolder;
        _folderPrefixLength = 4;
    }

    public void RenameFiles()
    {
        // Do stuff, uses all the class fields except destination folder
    }

    public void CopyFiles()
    {
        // Do stuff, also uses the class fields
    }
}

我很困惑,我是应该只向构造函数发送接口/依赖项,创建一些参数对象并将其传递给构造函数,还是保持原样,并在解析实例时传递参数。

那么,有没有一种更正确的方法来设置这个代码,以便与IOC容器一起工作?以下哪一项在设计上比我目前的布局更合适?

1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface IImageFileGenerator
{
    void RenameFiles(IList<IImageLink> links, string sourceFolder);
    void CopyFiles(IList<IImageLink> links, string sourceFolder, stringDestFolder);
}

public class ImageFileGenerator : IImageFileGenerator
{
    private readonly int _folderPrefixLength;

    public ImageFileGenerator()
    {
        _folderPrefixLength = 4;
    }

    public void RenameFiles(IList<IImageLink> links, string sourceFolder)
    {
        // Do stuff
    }

    public void CopyFiles(IList<IImageLink> links, string sourceFolder, stringDestFolder)
    {
        // Do stuff
    }
}

我不喜欢在这两种情况下传递完全相同的内容(除了目标文件夹)。在IImageFileGenerator的当前实现中,我需要执行这两个方法,并且每个方法都需要相同的值。这就是我通过构造函数传入状态的原因。

2。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public interface IImageFileGenerator
{
    void RenameFiles();
    void CopyFiles();
}

public class ImageLinkContext
{
    // various properties to hold the values needed in the
    // ImageFileGenerator implementation.
}

public class ImageFileGenerator : IImageFileGenerator
{
    private readonly IList<IImageLink> _links;
    private readonly string _sourceFolder;
    private readonly string _destinationFolder;
    private readonly int _folderPrefixLength;

    public ImageFileGenerator(ImageLinkContext imageLinkContext)
    {
        // could also use these values directly in the methods
        // by adding a single ImageLinkContext field and skip
        // creating the other fields
        _links = imageLinkContext.ImageLinks;
        _sourceFolder = imageLinkContext.Source;
        _destinationFolder = imageLinkContext.Destination;
        _folderPrefixLength = 4;
    }

    public void RenameFiles()
    {
        // Do stuff, uses all the class fields except destination folder
    }

    public void CopyFiles()
    {
        // Do stuff, uses all the class fields
    }
}

这种方法甚至可以调整为一个门面服务(以前称为聚合服务),如这里马克·西曼所提到的。

我也读过,您可以使用这些值的属性,并使用属性注入,尽管这似乎不再是首选(autopac提到构造函数注入是首选的…我相信在第2版中甚至删除了这个功能)。

或者,我已经阅读到您还可以创建一个初始化方法,并确保在其中设置了属性。

有这么多的选择,我越来越困惑,因为我读了更多关于这个东西。我确信没有明确的正确方法(或者可能有,至少在这个例子中是这样的)???)但是也许有人可以提供每种方法的优缺点。或许还有另一种方法我完全错过了。

我现在意识到这个问题可能有点主观(实际上不止一个问题),但我希望你能原谅我并提供一些指导。

PS-我目前正在尝试自动空调,以防影响哪种设计可能更适合。

注意:我对目标文件夹的代码做了一些细微的更改…Renamefiles没有使用它(可能与您的答案有关)。


好吧,我在阅读了.NET中的书籍依赖项注入(我强烈推荐这本书给任何面向对象的开发人员,不仅仅是.NET开发人员,也不仅仅是那些有兴趣使用IOC容器的开发人员!).

我现在在域程序集中得到了以下内容:

1
2
3
4
5
6
7
8
9
10
public interface IImageFileService
{
    void RenameFiles();
    void CopyFiles();
}

public interface IImageLinkMapRepository
{
    IList<ImageLink> GetImageLinks();
}

然后在FileAccess程序集中,我为这些接口创建了实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ImageFileService : IImageFileService
{
    public ImageFileService(IImageLinkMapRepository repository)
    {
        // null checks etc. left out for brevity
        _repository = repository;
    }

    public void RenameFiles()
    {
        // rename files, using _repository.GetImageLinks(), which encapsulates
        // enough information for it to do the rename operations without this
        // class needing to know the specific details of the source/dest dirs.
    }

    public void CopyFiles()
    {
        // same deal as above
    }
}

所以本质上,我已经消除了对构造函数中基本类型的需求,至少对于这个类是这样的。在某种程度上,我确实需要这些信息,但是这些信息被注入到ImageLinkMapRepository中,在那里信息更加合理。我用autopac命名参数来处理注入。

所以我想回答我自己的问题,如果原始构造函数参数有意义的话,它们是一个好主意,但是要确保将它们放在它们所属的位置。如果事情看起来不太顺利,重新考虑设计可能会有所改善。


在您的示例中,您实际传递的是依赖项,而且是类操作所需的数据。

在您的例子中,听起来像是方法RenameFiles()CopyFiles()对传递给它们的参数进行操作。考虑到它们的名称,我认为可以使用不同的参数调用ImageFileGenerator的单个实例上的方法。如果这是真的,那么参数应该在方法上调用自己而不是构造函数。

另一方面,如果在一个实例中,RenameFiles()CopyFiles()都只使用相同的参数调用一次,那么这些参数将是构造函数的很好的候选者。

我个人会尽量避免对所需依赖项进行属性注入——在这种情况下,构造函数注入更为合适。