C# 如何在内部访问修饰符上实现Setter依赖项注入

C# 如何在内部访问修饰符上实现Setter依赖项注入,c#,.net,.net-assembly,C#,.net,.net Assembly,我有一个正在构建的.NET程序集,我只希望最终用户能够看到几个类和方法。我正在使用Visual Studio 2015在framework 4.5中使用库类项目创建程序集。 该程序集将提供一个控制台来管理一些相互关联的后端服务和应用程序。大部分工作将在部件内进行,最终用户将看不到这些工作。 程序集命名空间中的大多数类都是内部的,因此最终用户看不见;但是程序集将提供一些最终用户可以交互的公共类和方法。 按照我的设想,程序集将提供一个公共静态类,最终用户将在其中传入程序集所需的参数,然后返回最终用户

我有一个正在构建的.NET程序集,我只希望最终用户能够看到几个类和方法。我正在使用Visual Studio 2015在framework 4.5中使用库类项目创建程序集。 该程序集将提供一个控制台来管理一些相互关联的后端服务和应用程序。大部分工作将在部件内进行,最终用户将看不到这些工作。 程序集命名空间中的大多数类都是内部的,因此最终用户看不见;但是程序集将提供一些最终用户可以交互的公共类和方法。 按照我的设想,程序集将提供一个公共静态类,最终用户将在其中传入程序集所需的参数,然后返回最终用户可以使用的对象。 例如,下面是一个伪代码模板,它描述了公共静态类在概念上的样子

public static class ControlClientStarter
{
    public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */)
    {
        // Some code that parses the parameters . . . 

        ISrvc_Controller accountController = new AccountSrvc_Controller( /*. . . */ );
        ISrvc_Controller contractController = new ContractSrvc_Controller( /*. . . */ );
        ISrvc_Controller imageController = new ImageSrvc_Controller( /*. . . */ );
        ICatalogApp_Controller calatlogController = new CatalogApp_Controller( /*. . . */ );
        IPortal_Controller portalController = new PortalSrvc_Controller( /*. . . */ );

        ISrvcManagerConsole srvcMngrConsole = new SrvceManagerConsole(
            accountController,
            contractController,
            imageController,
            calatlogController,
            portalController);

        return srvcMngrConsole;
    }
}
上面的代码演示了assembly.dll提供了一个名为ControlClientStarter的公共静态类,该类有一个名为GetSrvcManagerConsole()的公共静态方法(…)。 该静态方法接受多个参数,然后基于这些参数和各种逻辑,创建实现ISrvc_控制器接口的对象以及实现其他接口的其他对象。 这些对象被注入到srvcMngrConsole对象的构造函数中,并将其返回给最终用户

internal interface ISrvc_Controller { /*. . . */ }
internal interface ICatalogApp_Controller { /*. . . */ }
internal interface IPortal_Controller { /*. . . */ }

public interface ISrvcManagerConsole
{
    void DoThis( /*. . . */ );
    void StopThis( /*. . . */ );
    void GetThat( /*. . . */ );
    void PublishThat( /*. . . */ );
}
最终用户将使用与此概念类似的东西访问控制台

ISrvcManagerConsole clientMngrConsole = ControlClientStarter.GetSrvcManagerConsole(  /*. . . */ );
clientMngrConsole.DoThis(  /*. . . */ );
clientMngrConsole.StopThis(  /*. . . */ );
clientMngrConsole.GetThat(  /*. . . */ );
clientMngrConsole.PublishThat(  /*. . . */ );
控制台内部可能抽象地类似于此伪代码,其中传递到SrvcManagerConsole的参数被保存到srvcMngrConsole对象中的接口实现字段

internal class SrvcManagerConsole : ISrvcManagerConsole
{
    ISrvc_Controller accountController ;
    ISrvc_Controller contractController ;
    ISrvc_Controller imageController ;
    ICatalogApp_Controller calatlogController;
    IPortal_Controller portalController ;

    internal SrvcManagerConsole(
        ISrvc_Controller accountController,
        ISrvc_Controller contractController,
        ISrvc_Controller imageController,
        ICatalogApp_Controller calatlogController,
        IPortal_Controller portalController)            
    {
        this.accountController = accountController;
        this.contractController = contractController;
        this.imageController = imageController;
        this.calatlogController = calatlogController;
        this.portalController = portalController;
    }

    public void DoThis( /*. . . */ )
    {  /*. . . */ }
    public void StopThis( /*. . . */ )
    {  /*. . . */ }
    public void GetThat( /*. . . */ )
    {  /*. . . */ }
    public void PublishThat( /*. . . */ )
    {  /*. . . */ }

    // private and-or internal methods and properties . . . 
}
如您所见,我使用构造函数依赖项注入来避免紧耦合;但这有一个问题。如果将来我想添加MapPublishApp_控制器、VideoSrvc_控制器等等,该怎么办

如果我在第一个版本之后将它们添加到构造函数中,那么已经使用此程序集的其他代码也必须更改。 如果我添加另一个srvcemanagenconsole构造函数,该构造函数带有额外的参数,可以使用新的控制器,那么我不会破坏以前的代码,但是构造函数参数将开始变得太多。 另外,根据最终用户发送给ControlClientStarter类的GetSrvcManagerConsole()方法的参数,我可能不想使用所有控制器。如果我能有将控制器添加到字段的方法,那就更好了。 挑战在于我不希望最终用户能够访问这些字段。我只希望程序集实例化控制器类并分配隐藏字段。如果我在SrvceManagerConsole类的内部访问修饰符中给出这些赋值方法,那么GetSrvcManagerConsole()方法将无法看到它们,因为它是公共的

注意接口上的访问修饰符。唯一一个是公共的是由SrvcManagerConsole类实现的。其余部分是内部的,因为最终用户不应访问它们

internal interface ISrvc_Controller { /*. . . */ }
internal interface ICatalogApp_Controller { /*. . . */ }
internal interface IPortal_Controller { /*. . . */ }

public interface ISrvcManagerConsole
{
    void DoThis( /*. . . */ );
    void StopThis( /*. . . */ );
    void GetThat( /*. . . */ );
    void PublishThat( /*. . . */ );
}
上面的那些方法,比如PublishThat()将使用设置为注入构造函数的对象的字段。使用我的程序集的最终用户程序员永远不会看到这些接口实现实例。最终用户程序员创建的clientMngrConsole对象将使用这些内部对象,而程序员甚至不知道它们是如何使用的

实现带有内部访问修饰符的接口的类在抽象上是这样的

class AccountSrvc_Controller : ISrvc_Controller { /*. . . */ }
class ContractSrvc_Controller : ISrvc_Controller { /*. . . */ }
class ImageSrvc_Controller : ISrvc_Controller { /*. . . */ }
class CatalogApp_Controller : ICatalogApp_Controller { /*. . . */ }
class PortalSrvc_Controller : IPortal_Controller { /*. . . */ }
将来可能会有更多的控制器类,也许不会。 我一直无法弄清楚如何将控制器类分配给后端字段,以便最终用户无法访问这些字段,同时保持一定程度的松耦合,以防将来这些控制器的实现需要更改。 但是,我发现,如果其他方法和属性在isrvcemanagenconsole接口中声明,并且该接口使用公共修饰符设置,SrvceManagerConsole类中实现该接口的那些方法和属性也可以设置为public并成功访问,尽管SrvceManagerConsole类本身被设置为“internal” 我尝试了属性设置器注入,但C#(明智地)不支持带有接口后端的属性(因为这没有任何意义)。 另一种可能是让SrvceManagerConsole构造函数接受一个参数,该参数可能会将所有其他接口实例包装到字典中。我还没试过。 我不可能是第一个面对这件事的人。一定有一个我一直找不到的通用解决方案。 我知道行业标准是使用IoC容器来完成我上面所做的工作,但这不是在这里要做的。 我以前从未尝试过这样的架构。我知道这可能不是最好的架构;但目前,它不必完全符合某些过于复杂的体系结构。我将为一个适当的设计,因为我正在学习更多关于这一点

编辑#1,为什么构建器模式在这里不起作用

假设我试图通过使用构建器模式(如下面的伪代码)来避免构造函数反模式

假设SrvcManagerConsole如下所示:

internal class SrvcManagerConsole : ISrvcManagerConsole
{
    internal ISrvc_Controller accountController;

    internal SrvcManagerConsole() { }

    internal void AddAccountController(ISrvc_Controller accountController)
    {
        this.accountController = accountController;
    }
}
public static class ControlClientStarter
{
    public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */)
    {
        Federated_ControlBuilder fedBuilder  = new Federated_ControlBuilder();
        ISrvcManagerConsole srvcMngrConsole = fedBuilder.GetSrvcManagerConsole();
        return srvcMngrConsole;
    }
}
internal class Federated_ControlBuilder : IControl_Builder
{
    internal ISrvcManagerConsole srvcMngrConsole;

    internal Federated_ControlBuilder()
    {
        srvcMngrConsole = new SrvcManagerConsole();
    }

    public void InjectAccountController(ISrvc_Controller accountController)
    {
        // The srvcMngrConsole object can not see the InjectAccountController method.
        // srvcMngrConsole.
    }

    internal ISrvcManagerConsole GetSrvcManagerConsole()
    {
        return srvcMngrConsole;
    }
}
假设我将ControlClientStarter更改为如下内容:

internal class SrvcManagerConsole : ISrvcManagerConsole
{
    internal ISrvc_Controller accountController;

    internal SrvcManagerConsole() { }

    internal void AddAccountController(ISrvc_Controller accountController)
    {
        this.accountController = accountController;
    }
}
public static class ControlClientStarter
{
    public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */)
    {
        Federated_ControlBuilder fedBuilder  = new Federated_ControlBuilder();
        ISrvcManagerConsole srvcMngrConsole = fedBuilder.GetSrvcManagerConsole();
        return srvcMngrConsole;
    }
}
internal class Federated_ControlBuilder : IControl_Builder
{
    internal ISrvcManagerConsole srvcMngrConsole;

    internal Federated_ControlBuilder()
    {
        srvcMngrConsole = new SrvcManagerConsole();
    }

    public void InjectAccountController(ISrvc_Controller accountController)
    {
        // The srvcMngrConsole object can not see the InjectAccountController method.
        // srvcMngrConsole.
    }

    internal ISrvcManagerConsole GetSrvcManagerConsole()
    {
        return srvcMngrConsole;
    }
}
然后我实现联邦控制生成器,如下所示:

internal class SrvcManagerConsole : ISrvcManagerConsole
{
    internal ISrvc_Controller accountController;

    internal SrvcManagerConsole() { }

    internal void AddAccountController(ISrvc_Controller accountController)
    {
        this.accountController = accountController;
    }
}
public static class ControlClientStarter
{
    public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */)
    {
        Federated_ControlBuilder fedBuilder  = new Federated_ControlBuilder();
        ISrvcManagerConsole srvcMngrConsole = fedBuilder.GetSrvcManagerConsole();
        return srvcMngrConsole;
    }
}
internal class Federated_ControlBuilder : IControl_Builder
{
    internal ISrvcManagerConsole srvcMngrConsole;

    internal Federated_ControlBuilder()
    {
        srvcMngrConsole = new SrvcManagerConsole();
    }

    public void InjectAccountController(ISrvc_Controller accountController)
    {
        // The srvcMngrConsole object can not see the InjectAccountController method.
        // srvcMngrConsole.
    }

    internal ISrvcManagerConsole GetSrvcManagerConsole()
    {
        return srvcMngrConsole;
    }
}
Federated_ControlBuilder类的InjectAccountController(…)方法无法看到SrvcManagerConsole类的AddAccountController(…)方法,因为它是内部的

我问题的关键不在于如何解决