Объявление класса как члена самого себя - PullRequest
5 голосов
/ 04 июня 2009

Я недавно работал в Microsoft.Ink dll, используя C #, и отлаживал проблему (которая не связана с этим). Я заметил, что при отладке у чернильных объектов был объект штрихов, у которого был чернильный объект. , который имел .... и т. д.

Это смутило меня, так как я предполагал, что вы не сможете этого сделать (я из C ++ Background)

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

public sealed class Factory
{

    private static Factory instance = new Factory();
}

Как это вообще возможно? Теперь я могу назвать instance.instance.instance.instance ... и т. Д. Это, как вы можете себе представить, вредит моему смертному мозгу, и я уверен, что это не может быть хорошо и на компьютере. Как компилятор справляется с этим? И насколько глубоко заходит кроличья нора?

Ответы [ 9 ]

9 голосов
/ 04 июня 2009

Поскольку он статический и поэтому в домене приложения есть только одна копия переменной instance.

То, о чем вы думаете, это:

public class Foo
{
  private Foo lol = new Foo();
}

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

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

В примере OP присваивание требует построения, но присваивание инициируется статическим конструктором , а не конструктором экземпляра. Статический конструктор выполняется только один раз в AppDomain, чтобы инициализировать класс Type. Он не вызывается конструкцией экземпляра, поэтому (в примере с OP) не приведет к переполнению стека.

7 голосов
/ 04 июня 2009

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

class Directory
{
   string name;
   Directory parentDirectory;
}

Это просто позволяет объектам этого класса иметь внутреннюю ссылку на другой объект этого класса.

6 голосов
/ 04 июня 2009

Это программный шаблон, известный как " Singleton ".

Некоторые люди недовольны использованием шаблона по большему количеству причин, чем просто указано в вопросе, но, к лучшему или к худшему, это общий шаблон в .NET Framework. Вы найдете свойства (или поля) Singleton в классах, которые должны создаваться только один раз. Думайте о статическом свойстве Instance как о глобальном крюке для подвешивания объекта.

5 голосов
/ 04 июня 2009

Поскольку это класс, а не структура, когда вы объявляете поле, являющееся классом, вы определяете только ссылку на класс. Это позволяет сохранять ссылки при условии, что вы их присваиваете.

В вашем случае ваша ссылка выделяет новый класс, но он статический, поэтому он будет делать это только один раз, независимо от того, сколько классов вы создаете. Конструктор экземпляра запускается при первом использовании Factory и вызывает единственный нестатический конструктор. Выполнение instance.instance.instance запрещено, поскольку экземпляр статический. Вы не можете получить доступ к статической переменной из члена - вам нужно сделать Factory.instance.

Тем не менее, вы можете ~ сделать экземпляр нестатичным и иметь ссылку на какой-нибудь другой класс "Factory" или даже ссылку на него. В этом случае вы можете связать instance.instance.instance - но он будет просто следовать ссылкам, если вы их установили. Все работает, никаких проблем.

2 голосов
/ 04 июня 2009

Всегда будет только один экземпляр 'instance', потому что он статический. Единственный способ получить к нему доступ - позвонить в Factory.instance.

string text = Factory.instance.ToString(); // legal
string text2 = Factory.instance.instance.ToString(); // compiler error
1 голос
/ 04 июня 2009

Я думаю, что вы должны спросить наоборот: почему это не может быть возможно? Factory - это просто тип, подобный любому типу, который разрешается компилятором.

Поскольку большинство ответов здесь указывают на то, что это работает только потому, что Factory является статическим полем, я добавил следующий пример. Обратите внимание, что это очень примитивный пример связанного списка (вы, вероятно, не реализовали бы его таким образом по разным причинам, но лучшего примера я пока не нашел). В этом примере ChainedListItem - это контейнер для элемента односвязного списка, который содержит поле того же типа для указания следующего элемента в списке. В списке есть (пустой) элемент заголовка, а последний элемент отмечен пустым полем _nextItem:

public class ChainedListItem<T>
{
    private ChainedListItem<T> _nextItem;
    T _content;

    public ChainedListItem<T> NextItem
    {
        get { return _nextItem; }
        set { _nextItem = value; }
    }

    public T Content
    {
        get { return _content; }
        set { _content = value; }
    }

    public ChainedListItem<T> Add(T content)
    {
        _nextItem = new ChainedListItem<T>();
        _nextItem.Content = content;
        return _nextItem;
    }

    public void Dump()
    {
        ChainedListItem<T> current = this;
        while ((current = current.NextItem) != null)
        {
            Console.WriteLine(current._content);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        ChainedListItem<int> chainedList = new ChainedListItem<int>();
        chainedList.Add(1).Add(2).Add(3);
        chainedList.Dump();        
    }
}

«Кроличья нора» заходит так глубоко, как ваше пространство стека позволяет вам сделать еще один вызов конструктора типа. Если вы попытаетесь пойти еще глубже, вы получите исключение stackoverflow, как и в случае любой другой рекурсии.

Кстати, код, который вы написали в своем ответе, показывает очень простую реализацию Singleton , которая фактически основана на наличии (частного) статического члена того же типа, что и окружающий тип ,

И, наконец, что не менее важно, такие конструкции также прекрасно работают в C ++.

0 голосов
/ 04 июня 2009

Я не уверен, как вы могли бы получить доступ к экземпляру, так как он является частным. Единственное, для чего это было бы полезно, - это реализация Singleton, но если это так, вы представляете публичное свойство, раскрывающее экземпляр.

0 голосов
/ 04 июня 2009

Это синглтон . Это означает, что на самом деле существует только один экземпляр класса.

Это весь класс? Как правило, в C # вы увидите синглтон как

public class SomeClass
{

  static readonly SomeClass instance = new SomeClass();


        public static SomeClass Instance
        {
            get { return instance; }
        } 


        static SomeClass() 
        {
        }

        SomeClass()
        {
        }
}
0 голосов
/ 04 июня 2009

Это делается все время на большинстве ОО языков. Экземпляр является статическим членом Фабрики. В этом коде нет ничего необычного. Это стандартная фабричная модель. У вас также есть проблема с таким кодом?

x = x + 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...