Видимость конструктора вложенных классов

Есть ли способ ограничить создание экземпляра вложенного класса в С#? Я хочу предотвратить создание вложенного класса из любого другого класса, кроме класса вложенности, но чтобы обеспечить полный доступ к вложенному классу из другого кода.

Ответ 1

Обычно я создаю интерфейс для функций, которые вы хотите предоставить другим классам, а затем создайте вложенный класс private и реализуйте этот интерфейс. Таким образом, определение вложенного класса может оставаться скрытым:

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

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

Ответ 2

Короче говоря, нет, вы не можете этого сделать. Существует модификатор accessibity "public", который означает "доступный никем внутри меня или вне меня", и есть модификатор доступности "private", что означает "доступность чего-либо внутри меня". Не существует модификатора, который означает "доступный для вещи непосредственно вне меня, но не для чего-либо вне ее", и это то, что вам нужно будет пометить как конструктор. Это просто не концепция, о которой думали разработчики системы типов.

Можете ли вы описать, почему вам нужен этот сумасшедший вид доступности? Возможно, есть лучший способ получить то, что вы хотите.

Ответ 3

Если вам необходимо выполнить одно из следующих требований:

  • Вы хотите, чтобы вложенный класс был запечатан,
  • Вы не хотите копировать все вложенные подписи метода класса в интерфейс, например, в Ответ Ли,

Я нашел решение, подобное тому, которое было опубликовано 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 явно реализует метод CreateInstance из частного интерфейса IPrivateFactory, так что только те, кто может видеть IPrivateFactory, могут вызывать CreateInstance и получать новый экземпляр Nested.

Код вне класса Outer не может свободно создавать экземпляры Nested без запроса Outer.GetNested(), потому что

  • Конструктор
  • Outer.Nested приватен, поэтому он не может называть его напрямую
  • Outer.Nested.Factory может быть создан, но не может быть отброшен до IPrivateFactory, поэтому его метод CreateInstance() не может быть вызван.

Обратите внимание, что я бы не рекомендовал использовать этот шаблон в основном в производственном коде, но это трюк, который мне очень пригодился в редких случаях.

Ответ 4

Поскольку в синтаксисе С# ничего нет, вам нужно будет реализовать что-то вроде "контракта" между ними. Вы можете воспользоваться тем, что вложенный класс может получить доступ к закрытым полям своего родителя:

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;

Ответ 5

Для ответа, предложенного Джошуа Смитом, мне было необходимо заставить статический конструктор FriendClass запускаться, достигнув путем вызова пустого статического метода Initalize() на FriendClass из статического конструктора ParentClass.

Ответ 6

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

Обратите внимание, что вы можете получить доступ к закрытым членам Outer from Nested.

Ответ 7

UPDATE: неправильный ответ, см. комментарии

Внутренний модификатор - это то, что вы ищете:

public class OuterClass
{
    public class NestedClass {
        internal NestedClass()
        {

        }
    }
}

NestedClass будет виден всем, но конструктор будет доступен только для OuterClass.