.NET - 如何创建一个类,只有一个其他特定的类可以实例化它?

时间:2009-12-18 16:04:16

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

我想进行以下设置:

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类可以实例化一个参数。

执行此操作的一种方法是创建IParameter接口,然后在Descriptor类中使Parameter类成为私有,但在实际使用中,参数将具有多个属性,而我'我想避免重新定义它们两次。

这有可能吗?

6 个答案:

答案 0 :(得分:19)

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

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 */ }
    }
}

答案 1 :(得分:4)

LBushkin有正确的想法。如果您想避免重新键入所有属性,只需右键单击该类的名称,然后选择“重构”&gt; “Extract Interface”,它应该为您提供包含所有这些属性的接口。 (这在VS 2008中有效,我不知道早期版本。)

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

使用StrongNameIdentityPermission attributeSecurityAction.LinkDemand option将实例化(参数)标记为“受保护”类:

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

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

我在几个应用程序中使用过这种技术,但效果非常好。

希望这有帮助。

答案 5 :(得分:-1)

如果只希望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. }
  }  
}