Søg på DotNyt:
Denne blog er flyttet til www.nielsbrinch.com


lørdag den 27. september 2008

Alt om brug af enums

skrevet af Niels Brinch

Jeg har efterhånden arbejdet med mange projekter hvor der indgår enums på den ene eller den anden måde og jeg synes godt jeg kan komme med en kvalificeret anbefaling af hvordan de anvendes.

Men først det basale.

Hvad er en enum?

Man kan tænke på det som en afgrænset mængde valgmuligheder. I eksemplet herunder repræsenterer min enum de forskellige måder brugeren kan vælge at få sorteret søgeresultater.

public enum SortOptionType

{

    MostRecent = 0,

    Popular = 1,

    TalkedAbout = 2,

    HotRightNow = 3

}

Navnet slutter på "Type" i ovenstående eksempel. Det er der ingen konvention der siger den skal, men det plejer at give god mening at kalde den det - for det er typisk det den er. Men fald derimod ikke for fristelsen og giv den et flertalsnavn. Det giver kun mening når du lige sidder og skriver din enum, for så skriver du jo flere forskellige typer - men det giver bedre mening med ental senere hen når din enum anvendes.

Man behøver i øvrigt ikke eksplicit at skrive tal ud for hver enum-værdi, men det er god praksis at gøre hvis man vil konvertere mellem enums og tal, hvilket ofte kan være relevant - mere om det senere.

Hvordan anvendes den?

Enum-værdier er meget lette at sammenligne med hinanden og det giver let læselig kode. Hver enum-værdi repræsenterer et tal (oftest) - man kunne vælge bare at sammenligne talværdierne i stedet, men dagen efter har programmøren glemt hvad tallene betød.

For at gøre denne guide komplet, starter jeg fra bunden og viser hvordan man laver en property på en klasse til at gemme sin enum - men det gøres ligesom alle andre datatyper.

public class Navigation

{

    private SortOptionType sortOptionType;

 

    public SortOptionType SortOptionType

    {

        get { return sortOptionType; }

        set { sortOptionType = value; }

    }

}

Jeg anbefaler at navngive propertyen nøjagtig det samme som enum'en hedder - som i eksemplet herover, med mindre der er en god grund til at lade være.

Sådan assignes den valgte enum til propertyen.

navigation.SortOptionType = SortOptionType.MostRecent;

Her er et eksempel hvor jeg har anvendt ovenstående enum.

// Make the headline text to explain what is viewed

StringBuilder headline = new StringBuilder();

 

switch (navigation.SortOptionType)

{

    case SortOptionType.MostRecent:

        headline.Append("Most recent");

        break;

    case SortOptionType.Popular:

        headline.Append("Most popular");

        break;

    case SortOptionType.TalkedAbout:

        headline.Append("Most talked about");

        break;

    case SortOptionType.HotRightNow:

        headline.Append("Most hot right now");

        break;

}

Hvordan konverteres den?

En af de store styrker ved en enum er at den er en simpel datatype som let kan konverteres til tal og tilbage igen. Det er relevant i to situationer:

  • Hvis enum-værdien skal gemmes i databasen, er det fornuftigt at gemme tal-værdien af din enum - både fordi tallet er kortere end teksten, men bestemt også fordi databasen er langt bedre til at sortere og søge på tal end på tekst.
  • Hvis enum-værdien skal vælges af brugeren i brugergrænsefladen, skal den konverteres til enten tekst eller et tal - jeg anbefaler tal fordi det er lettere at arbejde med - og hverken du, som programmør, eller brugeren behøver nogensinde at blive præsenteret for tallet. Det kan være valg-værdierne i en rullemenu.

Så her er lidt konverteringer:

SortOptionType sortOptionType = SortOptionType.MostRecent;

 

// En enum konverteres til int ganske enkelt med et cast.

int i = (int)sortOptionType;

 

// Og det gælder også den anden vej.

sortOptionType = (SortOptionType)i;

 

// En enum kan let konverteres til streng, med ToString().

string str = sortOptionType.ToString();

 

// Men fra tekst til enum er knap så elegant

sortOptionType = (SortOptionType)Enum.Parse(typeof(SortOptionType), "MostRecent");

Her er i øvrigt en hyggelig lille metode til at vise alle enum-valgmuligheder i en rullemenu:

drdSortOptions.DataSource = Enum.GetNames(SortOptionType);

drdSortOptions.DataBind();

Placering i projektet

Følgende er de tre steder som udviklere typisk lægger sin enums - jeg anbefaler to af dem og forklarer hvorfor:

  1. "Den skal da ligge i den klasse hvor den hører til!":

    public class Navigation

    {

        // Klassens øvrige indhold

     

        public enum SortOptionType

        {

            MostRecent = 0,

            Popular = 1,

            TalkedAbout = 2,

            HotRightNow = 3

        }

    }


    Problemet med dette valg er, at en enum viser sig at være relevant i forhold til flere klasser og så ved programmøren pludselig ikke hvor den er. Jeg vil da ikke helt udelukke det KAN være fornuftigt, men jeg er aldrig stødt på et eksempel hvor det var en god idé.

  2. "Nej ok, men så placerer vi da bare alle vores enums i samme klasse!":

    public class Enums

    {

        public enum SortOptionType

        {

            MostRecent = 0,

            Popular = 1,

            TalkedAbout = 2,

            HotRightNow = 3

        }

    }


    Ikke dumt. Så kan man altid finde sin enum. Man skriver bare Enums. og får så en liste af alle de enums som eksisterer. Og har man rigtig mange enums i et meget stort projekt, kan man lave flere samlingsklasser og kategorisere dem på den måde.

    ...men hvordan bliver syntaksen så når man anvender sin enum?

    if (sortOptionType == Enums.SortOptionType.MostRecent)


    Der skal stå "Enums." frygteligt mange steder i koden, hvilket ikke gør det mere læsevenligt, hvilket ellers var idéen med en enum. Stadig, det er fint at placere sine enums i en klasse til det formål, men koden kan altså godt blive mere læsevenlig med metode 3.

  3. Man behøver ikke skrive noget foran sin enum når man anvender den hvis man lægger den uden for klasse. Jo, opret en klasse, men fjern klasse-definitionen og skriv bare enum-definitioner direkte deri.

    using System;

     

    namespace PopBusiness

    {

        public enum SortOptionType

        {

            MostRecent = 0,

            Popular = 1,

            TalkedAbout = 2,

            HotRightNow = 3

        }

     

        public enum ImageQuality

        {

            NotUsable = 0,

            TooSmall = 1,

            SeemsBad = 2,

            SeemsOk = 3,

            SeemsGood = 4

        }

    }

Men en lille advarsel! Metode 3 anbefaler jeg for læsevenlighedens skyld, men kommer du til at give en enum samme navn som noget der f.eks. eksisterer i .NET frameworket, så beder du om ballade. Der vil konstant komme misforståelser om hvor vidt du mente det ene eller det andet. Hvis du helt vil undgå den tvivl, så anvend metode 2.

Jeg vælger metode 3 fordi jeg går op i læsevenligheden af min kode, hvilket alle der anvender enums gør.

2 kommentarer

2 Kommentarer:

At 29. september 2008 kl. 09.47, Blogger Niels Brinch skrev...

Folk skriver kommentarer direkte til mig i stedet for her på bloggen. En henvendte sig med en kommentar om at ordet "Alt" i overskriften nok er at love lidt for meget.

Det er selvfølgelig rigtigt - der er mere at vide on Enums, men jeg vil hævde det jeg har nævnt er alt det vigtige.

 
At 5. september 2011 kl. 13.42, Blogger Thimes skrev...

Jeg er ikke helt enig i at dette er noget der bør ligge i koden. Type er noget som i min mening bør ligge i en database. Det som er en databases styrke er at have en liste over data og kunne levere disse på en strukturet måde.

Så det der i koden ser ud som dette,

public enum SortOptionType

{

MostRecent = 0,

Popular = 1,

TalkedAbout = 2,

HotRightNow = 3

}

Vil ligge i en database som

Id, SortOptionType
0 , MostRecent
1 , Popular
2 , TalkedAbout
3 , HotRightNow

og koden bør skrives så den generisk kan håndtere dette. Så hvis der en dag kommer en tilføjelse

Id, SortOptionType
4 , Oldnews

Så behøver man ikke rette i koden, kompliere koden. Mindre vedligeholdelse.

 

Send en kommentar

<< Tilbage


 
Til forsiden

Niels Brinch