【问题标题】:.NET - how to make a class such that only one other specific class can instantiate it?.NET - 如何创建一个只有一个其他特定类可以实例化它的类?
【发布时间】:2010-12-28 02:18:36
【问题描述】:

我想要以下设置:

class Descriptor
{
    public string Name { get; private set; }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection

    private Descrtiptor() { }
    public Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
}

class Parameter
{
    public string Name { get; private set; }
    public string Valuie { get; private set; }
}

一旦从 XML 文件加载,整个结构将是只读的。我想这样做,只有 Descriptor 类可以实例化一个 Parameter。

这样做的一种方法是创建一个IParameter 接口,然后在描述符类中将Parameter 类设为私有,但在实际使用中,参数将具有多个属性,我想避免重新定义它们两次。

这有可能吗?

【问题讨论】:

    标签: c# .net class c#-3.0 access-modifiers


    【解决方案1】:

    您可以使用标记为 Internal 的构造函数。

    这样它对程序集中的类是公共的,而对它之外的类是私有的。

    【讨论】:

      【解决方案2】:

      使其成为实现特定接口的私有嵌套类。然后,只有外部类可以实例化它,但任何人都可以使用它(通过接口)。示例:

      interface IParameter
      { 
          string Name { get; } 
          string Value { get; }
      }
      
      class Descriptor
      {
          public string Name { get; private set; }
          public IList<IParameter> Parameters { get; private set; }
      
          private Descriptor() { }
          public Descriptor GetByName(string Name) { ... }
      
          class Parameter : IParameter
          {
              public string Name { get; private set; }
              public string Value { get; private set; }
          }
      }
      

      如果你真的必须避免使用接口,你可以创建一个公共抽象类,它具有所有属性,但声明了一个受保护的构造函数。然后,您可以创建一个私有嵌套类,该类继承自只能由外部类创建的公共抽象,并将其实例作为基类型返回。示例:

      public abstract AbstractParameter
      { 
          public string Name { get; protected set; } 
          public string Value { get; protected set; }
      }
      
      class Descriptor
      {
          public string Name { get; private set; }
          public IList<AbstractParameter> Parameters { get; private set; }
      
          private Descriptor() { }
          public Descriptor GetByName(string Name) { ... }
      
          private class NestedParameter : AbstractParameter
          {
              public NestedParameter() { /* whatever goes here */ }
          }
      }
      

      【讨论】:

      • 这仍然允许您创建另一个 Parameter 子类并通过它进行实例化。让构造函数protected internal,也许吧。
      • @Martinho:你错了。一个常见的误解是,“受保护的内部”会阻止来自程序集外部的类进行子类化。您应该将其视为受保护的 OR 内部,而不是受保护的 AND 内部 - 即该类可以从程序集内部或外部的任何其他类派生,但只能在内部实例化。
      【解决方案3】:

      LBushkin 的想法是正确的。如果您想避免重新键入所有属性,只需右键单击类的名称并选择“重构”>“提取接口”,这应该会为您提供一个包含所有这些属性的接口。 (这适用于VS 2008,我不知道早期版本。)

      C# 通常采用的方法不是避免冗余代码,VS 只会帮助您更快地编写它。

      【讨论】:

        【解决方案4】:

        还有另一种方法:检查调用堆栈的调用类型。

        【讨论】:

        • 这样的运行时检查通常是不可取的——因为它们既昂贵(由于反射)又脆弱——如果添加了新的派生类,也应该能够实例化该类型。
        • 由于其他原因它们也很脆弱:方法内联任何人?
        【解决方案5】:

        如果您只希望 Descriptor 类实例化一个 Parameter,那么您可以使 Descriptor 类成为 Parameter 的嵌套类。 (不是相反)这是违反直觉的,因为容器或父类是嵌套类。

        public class Parameter  
        {  
          private Parameter() { }
          public string Name { get; private set; }
          public string Value { get; private set; }
          public static Parameter.Descriptor GetDescriptorByName(string Name)
          {
            return Parameter.Descriptor.GetByName(Name);
          }
          public class Descriptor
          { // Only class with access to private Parameter constructor
            private Descriptor() { // Initialize Parameters }
            public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection
            public string Name { get; private set; }
            public static Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
          }  
        }
        

        【讨论】:

          【解决方案6】:

          使用StrongNameIdentityPermission attributeSecurityAction.LinkDemand option 将要“保护”的类从实例化(参数)中标记出来:

          [StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey="...")]
          class Parameter
          {
              ...
          }
          

          您需要提供适当的公钥。因为您要求对 Parameterclass 进行链接时间(实际上是 JIT 时间)检查,这意味着它只能从使用与公共密钥匹配的私钥的强名称签名的程序集中使用您在上面的属性构造函数中提供的键。当然,您需要将Descriptor 类放在一个单独的程序集中并相应地给它一个强名称。

          我已经在几个应用程序中使用了这种技术,并且效果很好。

          希望这会有所帮助。

          【讨论】:

            猜你喜欢
            • 2019-09-24
            • 1970-01-01
            • 1970-01-01
            • 2012-07-07
            • 2020-04-20
            • 1970-01-01
            • 1970-01-01
            • 2023-02-16
            • 1970-01-01
            相关资源
            最近更新 更多