Vra

In Kathleen Dollard se onlangse blog post, sy bied'n interessante rede om te gebruik geneste klasse in .netto.Egter, sy noem ook dat FxCop nie soos geneste klasse.Ek is die veronderstelling dat die mense die skryf van FxCop reëls is nie dom nie, so daar moet redenasie agter dat die posisie nie, maar ek het nie in staat was om dit te vind.

Was dit nuttig?

Oplossing

Gebruik 'n sub-klas toe die klas jy nes is net nuttig vir die omringende klas. Byvoorbeeld, geneste klasse toelaat om so iets te skryf (vereenvoudig):

public class SortedMap {
    private class TreeNode {
        TreeNode left;
        TreeNode right;
    }
}

Jy kan 'n volledige definisie van jou klas te maak in een plek, het jy nie het om te spring deur enige PIMPL hoepels te definieer hoe jou klas werk, en die res van die wêreld het nie nodig om enigiets van jou implementering sien.

As die TreeNode klas was eksterne, jy sal óf moet maak al die velde public of maak 'n klomp van die get/set metodes om dit te gebruik. Die res van die wêreld sou 'n ander klas het besoedel hul IntelliSense.

Ander wenke

Van Sun se Java Tutorial:

Hoekom Gebruik Nested Klasse? Daar is verskeie dwingende redes vir die gebruik van geneste klasse, onder hulle:

  • Dit is 'n manier om logies te groepeer klasse wat net gebruik word in 'n plek.
  • Dit verhoog inkapseling.
  • Geneste klasse kan lei tot meer leesbaar en onderhou kode.

logiese indeling van klasse-As 'n klas is nuttig om net een ander klas, dan is dit logies om dit in te sluit in daardie klas en bymekaar te hou die twee. Nes so "helper klasse" maak hul pakket meer vaartbelyn.

Verhoogde inkapseling-Oorweeg twee top-vlak klasse, A en B, waar B benodig toegang tot lede van 'n wat andersins private sou verklaar word. Deur wegkruip klas B binne die klas A, kan 'n se lede private verklaar en B kan hulle toegang. Daarbenewens kan B homself weggesteek van die buitewêreld <-.. Dit geld nie vir implementering van sub-klasse C # 's, dit geld net vir Java

Meer leesbare, onderhou kode-Nesting klein klasse binne top-vlak klasse plaas die kode nader aan waar dit gebruik word.

Ten volle Lazy en draad-veilige Singleton patroon

public sealed class Singleton
{
    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

bron: http://www.yoda.arachsys.com/csharp/singleton. html

Dit hang af van die gebruik. Ek sou selde gebruik 'n Openbare geneste klas, maar gebruik Private geneste klasse al die tyd. 'N Private geneste klas gebruik kan word vir 'n sub-voorwerp wat bedoel is om gebruik te word net binne die ouer. 'N Voorbeeld hiervan sou wees as 'n HashTable klas bevat 'n private Entry voorwerp te stoor intern net.

As die klas is bedoel om gebruik te word deur die oproeper (ekstern), ek gewoonlik soos die maak van dit 'n aparte selfstandige klas.

In bykomend tot die ander bogenoemde redes, daar is nog 'n rede dat ek kan dink nie net aan geneste klasse gebruik, maar in werklikheid openbare geneste klasse. Vir diegene wat werk met verskeie generiese klasse wat dieselfde generiese tipe parameters deel, die vermoë om te verklaar 'n generiese naamruimte sou baie nuttig wees. Ongelukkig Net (of ten minste C #) ondersteun nie die idee van generiese name spaces. So ten einde dieselfde doel te bereik, kan ons generiese klasse gebruik om dieselfde doel te vervul. Neem die volgende voorbeeld klasse wat verband hou met 'n logiese entiteit:

public  class       BaseDataObject
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  class       BaseDataObjectList
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
:   
                    CollectionBase<tDataObject>
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseBusiness
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

public  interface   IBaseDataAccess
                    <
                        tDataObject, 
                        tDataObjectList, 
                        tBusiness, 
                        tDataAccess
                    >
        where       tDataObject     : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
        where       tBusiness       : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
        where       tDataAccess     : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}

Ons kan die handtekeninge van hierdie klasse te vereenvoudig deur gebruik te maak van 'n generiese naamruimte (geïmplementeer via geneste klasse):

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{

    public  class       BaseDataObject {}

    public  class       BaseDataObjectList : CollectionBase<tDataObject> {}

    public  interface   IBaseBusiness {}

    public  interface   IBaseDataAccess {}

}

Dan, deur middel van die gebruik van gedeeltelike klasse soos voorgestel deur Erik van Brakel in 'n vroeëre opmerking, jy kan die klasse te skei in afsonderlike sub-lêers. Ek beveel die gebruik van 'n Visual Studio uitbreiding soos NestIn te ondersteun nes die gedeeltelike klas lêers. Dit laat die "naamruimte" klas lêers te ook gebruik word om die sub-klas lêers te organiseer in 'n gids soos manier.

Byvoorbeeld:

Entity.cs

public
partial class   Entity
                <
                    tDataObject, 
                    tDataObjectList, 
                    tBusiness, 
                    tDataAccess
                >
        where   tDataObject     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
        where   tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
        where   tBusiness       : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
        where   tDataAccess     : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
}

Entity.BaseDataObject.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObject
    {

        public  DataTimeOffset  CreatedDateTime     { get; set; }
        public  Guid            CreatedById         { get; set; }
        public  Guid            Id                  { get; set; }
        public  DataTimeOffset  LastUpdateDateTime  { get; set; }
        public  Guid            LastUpdatedById     { get; set; }

        public
        static
        implicit    operator    tDataObjectList(DataObject dataObject)
        {
            var returnList  = new tDataObjectList();
            returnList.Add((tDataObject) this);
            return returnList;
        }

    }

}

Entity.BaseDataObjectList.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  class   BaseDataObjectList : CollectionBase<tDataObject>
    {

        public  tDataObjectList ShallowClone() 
        {
            var returnList  = new tDataObjectList();
            returnList.AddRange(this);
            return returnList;
        }

    }

}

Entity.IBaseBusiness.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseBusiness
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Entity.IBaseDataAccess.cs

partial class   Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{

    public  interface   IBaseDataAccess
    {
        tDataObjectList Load();
        void            Delete();
        void            Save(tDataObjectList data);
    }

}

Die lêers in die Visual Studio oplossing ontdekkingsreisiger sou dan georganiseer word as sulks:

Entity.cs
+   Entity.BaseDataObject.cs
+   Entity.BaseDataObjectList.cs
+   Entity.IBaseBusiness.cs
+   Entity.IBaseDataAccess.cs

En jy die generiese naamruimte soos die volgende sal implementeer:

User.cs

public
partial class   User
:
                Entity
                <
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess
                >
{
}

User.DataObject.cs

partial class   User
{

    public  class   DataObject : BaseDataObject 
    {
        public  string  UserName            { get; set; }
        public  byte[]  PasswordHash        { get; set; }
        public  bool    AccountIsEnabled    { get; set; }
    }

}

User.DataObjectList.cs

partial class   User
{

    public  class   DataObjectList : BaseDataObjectList {}

}

User.IBusiness.cs

partial class   User
{

    public  interface   IBusiness : IBaseBusiness {}

}

User.IDataAccess.cs

partial class   User
{

    public  interface   IDataAccess : IBaseDataAccess {}

}

En die lêers sou soos volg in die oplossing ontdekkingsreisiger georganiseer:

User.cs
+   User.DataObject.cs
+   User.DataObjectList.cs
+   User.IBusiness.cs
+   User.IDataAccess.cs

Die bogenoemde is 'n eenvoudige voorbeeld van die gebruik van 'n buitenste klas as 'n generiese naamruimte. Ek het "generiese name spaces" met 9 of meer tipe parameters in die verlede gebou. Met dié soort parameters gesinchroniseer oor die nege tipes wat al wat nodig is om die tipe parameters weet hou was vervelige, veral wanneer die toevoeging van 'n nuwe parameter. Die gebruik van generiese name spaces maak dat kode veel meer hanteerbare en leesbaar.

As ek verstaan Katheleen se artikel reg, sy stel om te gebruik geneste klas in staat wees om te skryf SomeEntity.Versameling in plaas van EntityCollection< SomeEntity>.In my mening is dit omstrede manier om te spaar jy'n paar te tik.Ek is redelik seker dat in die werklike wêreld aansoek versamelings sal'n verskil in die implementering, so jy sal nodig het om te skep'n aparte klas in elk geval.Ek dink dat die gebruik van die klas naam te beperk ander klas omvang is nie'n goeie idee.Dit besoedel intellisense en versterk afhanklikhede tussen klasse.Met behulp van namespace is'n standaard manier om te beheer klasse omvang.Maar ek vind dat die gebruik van geneste klasse soos in @hazzen kommentaar is aanvaarbaar, tensy jy het ton van geneste klasse wat is'n teken van slegte ontwerp.

Nog 'n gebruik nog nie genoem geneste klasse is die skeiding van generiese tipes. Byvoorbeeld, veronderstel 'n mens wil 'n paar generiese families van statiese klasse wat metodes met verskillende getalle van parameters kan neem, saam met waardes vir 'n paar van daardie parameters, en genereer afgevaardigdes met minder parameters het. Byvoorbeeld, een wil 'n statiese metode wat 'n Action<string, int, double> kan neem en lewer 'n String<string, int> wat die verskaf aksie verby 3.5 as die double sal noem nie; 'n mens kan ook graag 'n statiese metode wat 'n 'n Action<string, int, double> kan neem en lewer 'n Action<string>, verby 7 as die int en 5.3 as die double het. Die gebruik van generiese geneste klasse, kan 'n mens te reël om die metode aanroepingen wees iets soos:

MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);

of nie, want laasgenoemde tipes in elke uitdrukking kan afgelei word, selfs al is die voormalige kinders kan nie:

MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);

Die gebruik van die sub-generiese tipes maak dit moontlik om te sê wat afgevaardigdes van toepassing is op watter dele van die algehele tipe beskrywing.

Die geneste klasse kan gebruik word vir die volgende behoeftes:

  1. Klassifikasie van die data
  2. Wanneer die logika van die belangrikste klas is ingewikkeld en jy voel soos jy nodig ondergeskikte voorwerpe om die bestuur van die klas
  3. Wanneer jy dat die staat en die bestaan van die klas ten volle afhanklik van die omringende klas

Ek gebruik dikwels sub-klasse aan implementering detail weg te steek. 'n Voorbeeld van antwoord Eric Lippert se hier:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }
}

Hierdie patroon word nog beter met gebruik van generiese. Sien hierdie vraag vir twee oulike voorbeelde. So ek uiteindelik skryf

Equality<Person>.CreateComparer(p => p.Id);

in plaas van

new EqualityComparer<Person, int>(p => p.Id);

Ook ek kan 'n generiese lys van Equality<Person> het maar nie EqualityComparer<Person, int>

var l = new List<Equality<Person>> 
        { 
         Equality<Person>.CreateComparer(p => p.Id),
         Equality<Person>.CreateComparer(p => p.Name) 
        }

waar as

var l = new List<EqualityComparer<Person, ??>>> 
        { 
         new EqualityComparer<Person, int>>(p => p.Id),
         new EqualityComparer<Person, string>>(p => p.Name) 
        }

is nie moontlik nie. Dit is die voordeel van sub-klas erfgename van 'n ouer klas.

Nog 'n geval (van dieselfde aard - wegkruip implementering) is wanneer jy wil maak lede 'n klas se (velde, eienskappe, ens) slegs toeganklik vir 'n enkele klas:

public class Outer 
{
   class Inner //private class
   {
       public int Field; //public field
   }

   static inner = new Inner { Field = -1 }; // Field is accessible here, but in no other class
}

As nawfal genoemde implementering van Abstract Factory patroon, kan dit-kode word axtended bereik Klas Clusters patroon wat gebaseer is op Abstract Factory patroon.

Ek wil nes uitsonderings wat eie is aan 'n enkele klas, dws is. Diegene wat nog nooit van enige ander plek gegooi.

Byvoorbeeld:

public class MyClass
{
    void DoStuff()
    {
        if (!someArbitraryCondition)
        {
            // This is the only class from which OhNoException is thrown
            throw new OhNoException(
                "Oh no! Some arbitrary condition was not satisfied!");
        }
        // Do other stuff
    }

    public class OhNoException : Exception
    {
        // Constructors calling base()
    }
}

Dit help om jou projek lêers netjies en nie vol honderd stomp bietjie uitsondering klasse.

Hou in gedagte dat jy nodig het om die sub-klas te toets. As dit is private, sal jy nie in staat wees om dit te toets in isolasie nie.

Jy kan dit interne maak, al is, in samewerking met die InternalsVisibleTo skryf . Maar dit sou dieselfde wees as 'n private veld interne net vir toetsdoeleindes, wat ek oorweeg slegte self-dokumentasie.

So, wil jy dalk om net te implementeer private geneste klasse waarby lae kompleksiteit.

ja vir hierdie geval:

class Join_Operator
{

    class Departamento
    {
        public int idDepto { get; set; }
        public string nombreDepto { get; set; }
    }

    class Empleado
    {
        public int idDepto { get; set; }
        public string nombreEmpleado { get; set; }
    }

    public void JoinTables()
    {
        List<Departamento> departamentos = new List<Departamento>();
        departamentos.Add(new Departamento { idDepto = 1, nombreDepto = "Arquitectura" });
        departamentos.Add(new Departamento { idDepto = 2, nombreDepto = "Programación" });

        List<Empleado> empleados = new List<Empleado>();
        empleados.Add(new Empleado { idDepto = 1, nombreEmpleado = "John Doe." });
        empleados.Add(new Empleado { idDepto = 2, nombreEmpleado = "Jim Bell" });

        var joinList = (from e in empleados
                        join d in departamentos on
                        e.idDepto equals d.idDepto
                        select new
                        {
                            nombreEmpleado = e.nombreEmpleado,
                            nombreDepto = d.nombreDepto
                        });
        foreach (var dato in joinList)
        {
            Console.WriteLine("{0} es empleado del departamento de {1}", dato.nombreEmpleado, dato.nombreDepto);
        }
    }
}
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top