C#限制使用类

时间:2019-06-13 16:10:27

标签: c# design-patterns architecture

下面的代码完成了我想要的工作。 Main方法中的代码外观和行为完全符合要求。但是,如果Userer,Home和DropdownMenu2类只能由HeaderNavigationMenu使用,则可以防止其他开发人员尝试在HeaderNavigationMenu类之外使用它们,这将是更好的选择。此外,大多数文章都不愿公开所有内容。

问题: 在此情况下使用的设计模式是否合适?在这种情况下是否有更好,更可接受的使用方式?


编辑:进行此设计的原因。

  1. 我希望HeaderNavigationMenu的最终用户仅能够使用点表示法来获取可用选项的列表。该体系结构实现了此目标(例如:navigationMenu.DropdownMenu2.SelectOption3())
  2. 想要其他最终可能需要编辑代码的人,以了解UserMenu,Home和DropDownMenu2类是专门由HeaderNavigationMenu类实现的。

public class HeaderNavigationMenu
{
    public HeaderNavigationMenu()
    {
        UsersMenu = new UsersMenu();
        Home = new Home();
        DropdownMenu2 = new DropdownMenu2();
    }

    public UsersMenu UsersMenu { get; set; }
    public Home Home { get; set; }
    public DropdownMenu2 DropdownMenu2 { get; set; }
}

public class UsersMenu
{
  ...
}

public class Home
{
  ...
}

public class DropdownMenu2
{
  public void SelectOption3()
  {
    ...
  }
  ...
}

static void Main(string[] args)
{
  HeaderNavigationMenu navigationMenu = new HeaderNavigationMenu();
  navigationMenu.DropdownMenu2.SelectOption3();

  // The following code is an example of undesired capability; 
  // prefer if Home class could only be 
  // used by HeaderNavigationMenu class
  Home home = new Home();
}

4 个答案:

答案 0 :(得分:3)

限制对类构造函数的访问。如果将它们声明为“内部”,则这些类只能由您的代码创建。

答案 1 :(得分:2)

如果您希望防止UsersMenu外部的DropdownMenu2HomeHeaderNavigationMenu实例化,但仍与{{1}然后有一个巧妙的技巧可以实现此行为。您可以将公共嵌套类与私有构造函数一起使用,这些私有构造函数会静态初始化其自己的工厂方法。基本模板是:

HeaderNavigationMenu

在您的示例中实现了以下概念:

public class Outer{
  private static Func<Inner> _innerFactory;
  public Inner ExposedInner {get; private set;}

  public Outer(){
    // Force the static initializer to run.
    System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Inner).TypeHandle);

    // Call the newly created factory method instead of a regular constructor.
    ExposedInner = _innerFactory();
  }

  public class Inner {
    static Inner(){
      // Initialize Outer's static factory method.
      _innerFactory = () => new Inner();
    }

    // Inner cannot be instantiated (without reflection) because its constructor is private.
    private Inner(){}

    // This method is now exposed for anyone to use.
    public void DoStuff(){ Console.WriteLine("Did stuff"); }
  }
}

有了这个,您仍然可以使用内部类的所有公共属性,但是没有人可以从class Program { static void Main(string[] args) { HeaderNavigationMenu navigationMenu = new HeaderNavigationMenu(); navigationMenu.DropdownMenu2.SelectOption3(); // This line will no longer work because the constructors // for the inner classes are private. HeaderNavigationMenu.HomeImpl home = new HeaderNavigationMenu.HomeImpl(); Console.ReadKey(); } } public class HeaderNavigationMenu { //Private factory methods that are statically initialized private static Func<UsersMenuImpl> _createUsers; private static Func<DropdownMenu2Impl> _createDropdown; private static Func<HomeImpl> _createHome; public HeaderNavigationMenu() { //Force the static constructors to run System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(UsersMenuImpl).TypeHandle); System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(HomeImpl).TypeHandle); System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(DropdownMenu2Impl).TypeHandle); UsersMenu = _createUsers(); Home = _createHome(); DropdownMenu2 = _createDropdown(); } public UsersMenuImpl UsersMenu { get; set; } public HomeImpl Home { get; set; } public DropdownMenu2Impl DropdownMenu2 { get; set; } public class UsersMenuImpl { //Static constructor to make the class factory method static UsersMenuImpl() { _createUsers = () => new UsersMenuImpl(); } private UsersMenuImpl() { } } public class HomeImpl { //Static constructor to make the class factory method static HomeImpl() { _createHome = () => new HomeImpl(); } private HomeImpl() { } } public class DropdownMenu2Impl { //Static constructor to make the class factory method static DropdownMenu2Impl() { _createDropdown = () => new DropdownMenu2Impl(); } private DropdownMenu2Impl() { } public void SelectOption3() { } } } 外部实例化内部类,只有HeaderNavigationMenu可以访问内部类。工厂方法。

答案 2 :(得分:1)

我不太了解您的用例,也从未这样编码,但是仅公开ENCODING所需行为的一种方法是将类内部化,并将变量设为私有,然后如下所示,仅公开HeaderNavigationMenu方法。

如果您取消注释该行

SelectOption3()

您将收到编译器错误。

//Home home = new Home();

答案 3 :(得分:1)

您可以创建UsersMenuHomeDropdownMenu2 public abstract类。然后将private类嵌套在HeaderNavigationMenu内部,以扩展public abstract版本。

public abstract class UsersMenu
{
}

public abstract class Home
{
}

public abstract class DropdownMenu2
{
   public void SelectOption3()
   {
      // Code for SelectOption3...
   }
}

public class HeaderNavigationMenu
{
    public HeaderNavigationMenu()
    {
        UsersMenu = new UsersMenuImpl();
        Home = new HomeImpl();
        DropdownMenu2 = new DropdownMenu2Impl();
    }

    public UsersMenu UsersMenu { get; }
    public Home Home { get; }
    public DropdownMenu2 DropdownMenu2 { get; }

    private class UsersMenuImpl : UsersMenu
    {
    }

    private class HomeImpl : Home
    {
    }

    private class DropdownMenu2Impl : DropdownMenu2
    {
    }
}

高级开发人员可以查看和使用UsersMenuHomeDropdownMenu2 abstract类,但是不能创建它们的实例。只有HeaderNavigationMenu可以。

当然,另一个开发人员总是可以创建从public abstract派生的自己的类,但是您只能做很多事情。 UsersMenuHomeDropdownMenu2必须是public,才能成为public属性。