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

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


person Bogi    schedule 29.04.2010    source источник
comment
Похоже, вы заново изобретаете шаблон проектирования Singleton.   -  person Bastiaan Linders    schedule 29.04.2010
comment
@Bastiaan: ты имеешь в виду implementing. Вы не изобретаете заново шаблоны проектирования....   -  person James    schedule 29.04.2010
comment
Объявите члены, которые вы не хотите делать доступными, включая конструктор, внутренними. Это значение по умолчанию.   -  person Hans Passant    schedule 29.04.2010
comment
Проблема с internal заключается в том, что он по-прежнему оставляет эти члены доступными для других типов в сборке. Что нужно C#, так это родительская видимость, которая разрешает доступ только из типа, включающего вложенный тип.   -  person Chris Charabaruk    schedule 09.02.2011


Ответы (6)


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

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

    public IFace GetNested()
    {
        return new Nested();
    }
}
person Lee    schedule 29.04.2010

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

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

person Eric Lippert    schedule 29.04.2010
comment
Внезапно (потому что это меня беспокоит): Реализация IAsyncResult. - person Chris Charabaruk; 09.02.2011
comment
Неправильно, см. мой ответ. - person MatteoSp; 05.11.2013
comment
@MatteoSp: Хотя я нахожу восхитительным, что вы думаете, что я ошибаюсь в отношении функции, которую я реализовал, я уверяю вас, что я прав в своем утверждении, что не существует модификатора доступности, который соответствовал бы описанию, описанному в исходном плакате. - person Eric Lippert; 05.11.2013

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

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

Я нашел решение, похожее на сообщенное 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(), потому что

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

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

person Suzanne Soy    schedule 02.02.2014

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

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;
person Andrej Kovacik    schedule 23.01.2014
comment
для меня статический конструктор внутреннего класса не вызывается, и я получаю исключение nullreferenceexception - person Austin_Anderson; 16.09.2017

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

person Martin Fay    schedule 27.04.2016
comment
Вы должны прокомментировать ответ Джошуа, а не публиковать новый ответ, который на самом деле им не является. - person Nicolas B. - SonarSource Team; 27.04.2016

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 из Nested.

person drowa    schedule 07.11.2017