2008-11-05 2 views
29

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

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

, который генерирует эту ошибку от компилятора:

Круговая базовая зависимость класса с участием 'A' и 'AB'

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

+2

Интересно, есть ли какая-то особая причина, по которой вы хотели бы это сделать, или вы опубликовали ее для обсуждения и обучения? Каким было бы практическое применение, если это было возможно? – Daan 2008-11-05 16:11:43

+0

@ Daan при внедрении свободного шаблона шаблона generic builder, у меня есть интерфейсы в общем классе, который я хочу реализовать в этом же классе. из-за этой проблемы я должен переместить интерфейсы в отдельный класс (должен быть в классе, чтобы они могли делиться общими типами и ограничениями). это сделало явную реализацию интерфейса крайне уродливой, так как я должен ссылаться на этот другой класс. (На самом деле я избегал ошибки и уродства, наследуя от этого другого класса ...) – 2016-08-29 02:10:15

ответ

32

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

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

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

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

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

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

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

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

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

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

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

+0

Я смущен тем, как это сделает это действительным. A расширяет B и B не имеет вложенного класса A или что-то еще ... – Tinister 2008-11-05 22:22:15

+0

Кроме того, если B расширенный A, этот тип вещей уже возможен: A.B.B.B.B.B.B.B.B.B.B.B.B.Foo(); – Tinister 2008-11-05 22:24:09

+0

Tinister: Вы правы, я привел пример неправильно :) Будет редактировать. – 2008-11-05 22:36:06

-2

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

0

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

13

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

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

0

Что касается вопросов о том, что я пытался сделать:

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

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

    private AA _containedObject; 
} 

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

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

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

0

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

Вместо:

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() { 
     ... 
    } 
} 
Смежные вопросы