简体   繁体   English

C#限制使用类

[英]C# restricting use of a class

The below code does what I would like it to do. 下面的代码完成了我想做的事情。 The code in the Main method looks and behaves exactly as desired. Main方法中的代码外观和行为完全符合要求。 However, it would be preferable if the class UserMenu, Home and DropdownMenu2 could only be used by the HeaderNavigationMenu to protect other developers from trying to used them outside of the HeaderNavigationMenu class. 但是,如果Userer,Home和DropdownMenu2类只能由HeaderNavigationMenu使用,则可以防止其他开发人员尝试在HeaderNavigationMenu类之外使用它们,这将是更好的选择。 Additionally most articles frown upon making everything public. 此外,大多数文章都不愿公开所有内容。

Question : Is the design patter being used below appropriate or is there something better and more acceptable to use in this scenario? 问题 :在以下情况下使用的设计模式是否适当?在这种情况下是否有更好,更可接受的使用方式?


Edit: The reason for this design. 编辑:这种设计的原因。

  1. I wanted the end user of HeaderNavigationMenu to just be able to use the dot notation to get a list of available options. 我希望HeaderNavigationMenu的最终用户仅能够使用点表示法来获取可用选项的列表。 This Architecture accomplishes this goal (ex: navigationMenu.DropdownMenu2.SelectOption3()) 该体系结构实现了此目标(例如:navigationMenu.DropdownMenu2.SelectOption3())
  2. Wanted anyone else who eventually might need to edit the code to understand that the classes UserMenu, Home and DropDownMenu2 where very specifically designed to be implemented by HeaderNavigationMenu class. 希望其他最终需要编辑代码的人了解类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();
}

Restrict access to the class constructors. 限制对类构造函数的访问。 If they are declared as "internal" then the classes may only be created by your code. 如果将它们声明为“内部”,则这些类只能由您的代码创建。

If you're looking to protect against the instantiation of UsersMenu , DropdownMenu2 , and Home from outside HeaderNavigationMenu but still within the same project as HeaderNavigationMenu then there is a neat trick that can achieve this behavior. 如果您正在寻找抵御的实例UsersMenuDropdownMenu2Home从外部HeaderNavigationMenu但仍然是相同的项目作为内HeaderNavigationMenu然后有一个巧妙的方法,可以实现这种行为。 You can use public nested classes with private constructors which statically initialize their own factory methods. 您可以将公共嵌套类与私有构造函数一起使用,这些私有构造函数会静态初始化其自己的工厂方法。 The basic template for this would be: 基本模板如下:

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"); }
  }
}

Here's this concept implemented in your example: 这是您的示例中实现的概念:

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()
        {
        }
    }
}

With this, you will still be able to use all the public properties of the inner classes however no one will be able to instantiate the inner classes from outside HeaderNavigationMenu and only HeaderNavigationMenu has access to the factory methods. 这样,您仍然可以使用内部类的所有公共属性,但是没有人可以从HeaderNavigationMenu外部实例化内部类,只有HeaderNavigationMenu可以访问工厂方法。

I don't really understand what your use case is and I've never coded like this but one way of only exposing the required behaviour of HeaderNavigationMenu would be to make the classes internal and the variables private and then expose only the SelectOption3() method, as below. 我真的不明白你的使用情况是什么,我从来没有编码这样的,但只露出所需的行为的一种方式HeaderNavigationMenu是使类内部和私有变量,然后仅露出SelectOption3()方法, 如下。

If you uncomment the line 如果您取消注释该行

//Home home = new Home();

you will get a compiler error. 您将得到一个编译器错误。

class Program
{
    static void Main(string[] args)
    {
        HeaderNavigationMenu navigationMenu = new HeaderNavigationMenu();
        navigationMenu.DropdownMenu2SelectOption3();

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

    public HeaderNavigationMenu()
    {
        usersMenu = new UsersMenu();
        home = new Home();
        dropdownMenu2 = new DropdownMenu2();
    }

    public void DropdownMenu2SelectOption3()
    {
        dropdownMenu2.SelectOption3();
    }

    class UsersMenu
    {
    }

    class Home
    {
    }

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

You could make UsersMenu , Home , and DropdownMenu2 public abstract classes. 您可以将UsersMenuHomeDropdownMenu2 Home public abstract类。 Then have private classes nested inside of HeaderNavigationMenu which extend the public abstract versions. 然后将private类嵌套在HeaderNavigationMenu内部, 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
    {
    }
}

Fellow developers can see and use the UsersMenu , Home , and DropdownMenu2 abstract classes, but cannot create instances of them. 相同的开发人员可以查看和使用UsersMenuHomeDropdownMenu2 abstract类,但不能创建它们的实例。 Only HeaderNavigationMenu can. 只有HeaderNavigationMenu可以。

Of course, another developer could always create their own classes deriving from the public abstract ones, but there is only so much you can do. 当然,另一位开发人员总是可以从public abstract类派生自己的类,但是您只能做很多事情。 UsersMenu , Home , and DropdownMenu2 have to be public in order to be public properties. UsersMenuHomeDropdownMenu2必须是public的才能成为public属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM