嵌套类构造函数的可见性

17

在 C# 中有没有一种方法可以限制嵌套类的实例化?我想防止从任何其他类实例化嵌套类,但允许其他代码完全访问嵌套类。


听起来你正在重新发明单例设计模式。 - Bastiaan Linders
1
@Bastiaan:你的意思是“实现”。你不需要重新发明设计模式... - James
声明那些你不想让其它人访问的成员,包括构造函数,使用 internal 关键字。这是默认设置。 - Hans Passant
13
internal 的问题在于它仍然允许同一程序集中的其他类型访问这些成员。C# 需要的是一种 "父级" 可见性,只允许封闭嵌套类型的类型访问该类型。 - Chris Charabaruk
7个回答

37

通常我会为想要向其他类公开的功能创建一个接口,然后将嵌套类设置为私有并实现该接口。这样,嵌套类定义就可以保持隐藏:

public class Outer
{
    private class Nested : IFace
    {
        public Nested(...)
        {
        }
        //interface member implementations...
    }

    public IFace GetNested()
    {
        return new Nested();
    }
}

12

如果您需要满足以下要求之一:

  • 您希望嵌套类被封闭,
  • 您不想像Lee的答案中那样将所有嵌套类的方法签名复制到接口中,

我找到了一个类似于ak99372发布的解决方案的解决方法,但没有使用静态初始化程序:

public class Outer
{
    private interface IPrivateFactory<T>
    {
        T CreateInstance();
    }

    public sealed class Nested
    {
        private Nested() {
            // private constructor, accessible only to the class Factory.
        }

        public class Factory : IPrivateFactory<Nested>
        {
            Nested IPrivateFactory<Nested>.CreateInstance() { return new Nested(); }
        }
    }

    public Nested GetNested() {
        // We couldn't write these lines outside of the `Outer` class.
        IPrivateFactory<Nested> factory = new Nested.Factory();
        return factory.CreateInstance();
    }
}

想法是 Nested 类的构造函数只能被嵌套一级更深的 Factory 类访问。 Factory 类明确实现了来自私有接口 IPrivateFactory 的方法 CreateInstance,因此只有那些可以看到 IPrivateFactory 的人才能调用 CreateInstance 并获得一个新的 Nested 实例。
Outer 类之外的代码不能自由地创建 Nested 的实例而不经过 Outer.GetNested(),因为:
  1. Outer.Nested 的构造函数是私有的,所以它们不能直接调用它
  2. Outer.Nested.Factory 可以被实例化,但无法强制转换为 IPrivateFactory,因此无法调用其 CreateInstance() 方法。
请注意,我不建议在生产代码中频繁使用该模式,但这是我在极少数情况下觉得有用的技巧。

8
简而言之,不行。有一个可访问性修饰符“public”,它的意思是“可以被我内部或外部的任何东西访问”,还有一个可访问性修饰符“private”,它的意思是“只能被我内部的任何东西访问”。没有一个修饰符意味着“可被我外面的东西立即访问但不能被它外面的任何东西访问”,这就是你需要标记构造函数的地方。那只是类型系统设计者认为没有用处的概念。
您能描述一下为什么您需要这种疯狂的可访问性吗?也许有更好的方法来实现您想要的功能。

1
从我的角度来看(因为这是我关注的问题):实现IAsyncResult。 - Chris Charabaruk

4

由于C#语法中没有这样的功能,你需要实现类似于“合同”的东西来解决这个问题。你可以利用嵌套类可以访问其父类的私有字段这一事实:

public class ParentClass
{
  private static Func<FriendClass> _friendContract;

  public class FriendClass
  {
      static FriendClass()
      {
          _friendContract= () => new FriendClass();
      }

      private FriendClass() { }
  }

  ///Usage
  public FriendClass MethodUse()
  {
      var fInstance = _friendContract();
      //fInstance.DoSomething();
      return fInstance;
  }
}

当然可以调整合同以处理不同的参数。

 private static Func<Arg1,Arg2,FriendClass> _friendContract;

1
对我来说,内部类的静态构造函数没有被调用,我得到了一个空引用异常。 - Austin_Anderson

2

使用 C# 11 的新静态抽象接口成员,您可以相当简洁地限制嵌套类的实例化:

public class Outer
{
    protected interface INestedFactory<T> where T : INestedFactory<T>
    {
        public static abstract T CreateInstance();
    }

    public class SomeNested : INestedFactory<SomeNested>
    {
        private SomeNested() { }

        static SomeNested INestedFactory<SomeNested>.CreateInstance()
        {
            return new SomeNested();
        }
    }

    protected void CreateNested<T>() where T : INestedFactory<T>
    {
        T.CreateInstance();
    }
}

2

对于Joshua Smith提出的答案,我发现有必要强制运行FriendClass的静态构造函数,可以通过在ParentClass的静态构造函数中调用FriendClass的空静态Inititalize()方法来实现。


你应该评论Joshua的答案,而不是发布一个并不真正回答问题的新答案。 - Nicolas B.

0
public class Outer
{
    public class Nested
    {
        readonly Outer Outer;

        public Nested(Outer outer /* , parameters */)
        {
            Outer = outer;

            // implementation
        }

        // implementation
    }

    public Nested GetNested(/* parameters */) => new Nested(this /* , parameters */);
}

请注意,您可以从嵌套类访问外部类的私有成员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接