Domanda

Ad esempio:

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

Che genera questo errore dal compilatore:

  

Dipendenza della classe base circolare   che coinvolge "A" e "A.B"

Ho sempre pensato che una classe nidificata si comportasse esattamente come una classe normale, tranne con regole speciali relative all'accesso ai membri privati ??della classe esterna, ma immagino che ci sia un'eredità implicita che si verifica tra le due classi?

È stato utile?

Soluzione

Non c'è eredità implicita per quanto ne so. Mi sarei aspettato che andasse bene, anche se posso immaginare stranezze se A e B fossero generici.

È specificato nella sezione 10.1.4 delle specifiche:

  

Quando una classe B deriva da una classe A,   è un errore in fase di compilazione da A a   dipende da B. Una classe dipende direttamente   sulla sua classe base diretta (se presente) e    dipende direttamente dalla classe all'interno   che viene immediatamente nidificato (if   qualunque). Data questa definizione, il   set completo di classi su cui a   la classe dipende è il transitivo   la chiusura del dipende direttamente   rapporto.

Ho evidenziato la sezione pertinente.

Questo spiega perché il compilatore lo sta rifiutando, ma non perché la lingua lo proibisce. Mi chiedo se ci sia una restrizione CLI ...

EDIT: Ok, ho avuto una risposta da Eric Lippert. Fondamentalmente, sarebbe tecnicamente possibile (non c'è nulla nella CLI per proibirlo), ma:

  • Permetterlo sarebbe difficile nel compilatore, invalidando varie ipotesi attuali su ordini e cicli
  • È una decisione di progettazione piuttosto strana che è più facile proibire che supportare

È stato anche notato sul thread dell'email che renderebbe valido questo tipo di cose:

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

... ma questo sarebbe già (come notato da Tinister) valido se B derivasse da A.

Annidamento + eredità = stranezza ...

Altri suggerimenti

Questa non è una cosa C # tanto quanto è una cosa del compilatore. Uno dei lavori di un compilatore è di disporre una classe in memoria, ovvero un gruppo di tipi di dati di base, puntatori, puntatori a funzione e altre classi.

Non può costruire il layout per la classe A fino a quando non sa quale sia il layout della classe B. Non può sapere quale sia il layout della classe B finché non termina con il layout della classe A. Dipendenza circolare.

Penso che l'annidamento significhi che il tipo nidificato è parte della definizione del tipo di annidamento. Con questa interpretazione, la limitazione ha senso perché al momento in cui il compilatore raggiunge la definizione di A, A.B non è ancora stato definito, e anche alla fine di A, è già definito in termini di A.B.

Per quanto riguarda le domande su cosa stavo tentando di fare:

In sostanza, volevo creare una classe che avesse una relazione di composizione con se stessa, ma non volevo avere l'oggetto contenuto per contenere altri oggetti e quindi creare una catena con molti " A has-a A has- a A ha-a A ha-a ... " relazioni. Quindi il mio pensiero all'epoca era fare qualcosa del genere:

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

    private AA _containedObject;
}

Che al momento sembrava piuttosto fluido ma a posteriori non ne sono così sicuro ...

Ho rovistato su Google e non ho trovato nessuna discussione a riguardo, quindi ho pensato di pubblicarlo qui.

Tuttavia, nei commenti di una post sul blog di Eric Lippert fornisce esempi di una classe che implementa un'interfaccia nidificata e una classe che implementa un'interfaccia generica con una classe nidificata come argomento type (che non viene compilato e lui chiama un "bug" nel compilatore corrente). Entrambi questi esempi riguardano le interfacce, quindi mi chiedevo se c'erano delle regole speciali con le classi nidificate. E sembra che ci siano.

Sono stato in grado di evitarlo (almeno con le interfacce) ereditando da una classe separata contenente le interfacce nidificate. (Nel mio scenario sto anche restituendo riferimenti a queste interfacce.)

Invece di:

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

Fai qualcosa del genere:

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() {
      ...
   }
}

Per evitare la bruttezza con implementazioni esplicite dell'interfaccia, puoi anche ereditare dall'altra classe, anche se ciò non funzionerebbe se stavi cercando di ereditare da una classe nidificata, poiché non puoi ereditare da entrambe le classi.

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() {
      ...
   }
}

Questo non ha senso per me ... Stai cercando di estendere qualcosa che non esiste !!! La classe B esiste solo nell'ambito della classe A e per questo penso che ci sia un qualche tipo di eredità.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top