Подтвердить что ты не робот

Почему класс не может расширить свой собственный вложенный класс в С#?

Например:

public class A : A.B
{
    public class B { }
}

Что генерирует эту ошибку из компилятора:

Циклическая зависимость базового класса с участием "A" и "A.B"

Я всегда считал, что вложенный класс ведет себя как обычный класс, за исключением специальных правил, касающихся доступа к закрытым членам внешнего класса, но я предполагаю, что существует какое-то неявное наследование между двумя классами?

4b9b3361

Ответ 1

На мой взгляд, никакого явного наследования не существует. Я бы ожидал, что все будет в порядке - хотя я могу представить себе странность, если A и B были родовыми.

Он указан в разделе 10.1.4 спецификации:

Когда класс B происходит от класса A, это ошибка времени компиляции для A зависят от B. Класс напрямую зависит по его прямому базовому классу (если есть) и напрямую зависит от класса внутри который он сразу вложен (если Любые). Учитывая это определение, полный набор классов, на которых класс зависит от переходного закрытие напрямую зависит от отношения.

Я выделил соответствующий раздел.

Это объясняет, почему компилятор отвергает его, но не почему язык запрещает его. Интересно, есть ли ограничение CLI...

EDIT: Хорошо, у меня был ответ от Эрика Липперта. В принципе, это было бы технически возможно (в CLI ничего не запретить), но:

  • Допустим, что в компиляторе это будет сложно, что приведет к недействительности различных текущих допущений по порядку и циклам.
  • Это довольно странное дизайнерское решение, которое проще запретить, чем поддерживать

В потоке электронной почты также было отмечено, что это делает такое действие действительным:

A.B x = new A.B.B.B.B.B.B.B.B.B.B.B.B();

... но это уже было бы (как отмечено Tinister) справедливо, если B получен из A.

Вложение + наследование = нечетность...

Ответ 2

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

Он не может построить макет для класса A, пока не узнает, что такое макет класса B. Он не может знать, каков макет класса B, пока он не закончится с макетом класса A. Круговая зависимость.

Ответ 3

Я думаю, что вложение предназначено для представления того, что вложенный тип является частью определения типа вложенности. С этой интерпретацией ограничение имеет смысл, поскольку в момент, когда компилятор попадает в определение A, A.B еще не определен, и даже в конце A он уже определен в терминах A.B.

Ответ 4

Относительно вопросов о том, что я пытаюсь сделать:

В принципе, я хотел создать класс, у которого было соотношение композиции с самим собой, но я не хотел, чтобы содержащийся объект содержал другие объекты и поэтому создал цепочку со многими "A has-a A has-a A имеет отношения A has-a...". Так что моя мысль в то время делала что-то вроде этого:

public class A : A.AA
{
    public class AA
    {
        // All of the class logic
    }

    private AA _containedObject;
}

Который в то время казался довольно гладким, но в ретроспективе я не так уверен...

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

Однако в комментариях post в блоге Eric Lippert приводятся примеры класса, реализующего вложенный интерфейс, а также класс реализуя общий интерфейс с вложенным классом как аргумент типа (который не компилируется, и он называет "ошибку" в текущем компиляторе). Оба эти примера касаются интерфейсов, поэтому мне было интересно, существуют ли какие-то специальные правила с вложенными классами. И кажется, что есть.

Ответ 5

Мне удалось избежать этого (по крайней мере с интерфейсами), наследуя от отдельного класса, содержащего вложенные интерфейсы. (В моем сценарии я также возвращаю ссылки на эти интерфейсы.)

Вместо:

public class MyClass<T1, T2, T3> :
   MyClass<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }

   Interface Interface.SomeMethod() {
      ...
   }
}

// compile error: Circular base class dependency

Сделайте что-то вроде этого:

public sealed class MyClassInterfaces<T1, T2, T3>
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }
}

sealed class MyClass<T1, T2, T3> :
   MyClassInterfaces<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   MyClassInterfaces<T1, T2, T3>.Interface
   MyClassInterfaces<T1, T2, T3>.Interface.SomeMethod() {
      ...
   }
}

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

public abstract class MyClassInterfaces<T1, T2, T3>
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }
}

sealed class MyClass<T1, T2, T3> :
   MyClassInterfaces<T1, T2, T3>,
   MyClassInterfaces<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   Interface Interface.SomeMethod() {
      ...
   }
}

Ответ 6

Это не имеет никакого смысла для меня... Вы пытаетесь продлить то, чего не существует!!! Класс B существует только в области класса A, и из-за этого я думаю, что существует какое-то наследование.