Dela via


Översikt över allmänna typer

Utvecklare använder generika hela tiden i .NET, oavsett om de är implicita eller explicita. När du använder LINQ i .NET har du någonsin märkt att du arbetar med IEnumerable<T>? Eller om du någonsin har sett ett onlineexempel på en "generisk lagringsplats" för att prata med databaser med Entity Framework, såg du att de flesta metoder returnerar IQueryable<T>? Du kanske har undrat vad T är i dessa exempel och varför det finns där.

Först introducerades i .NET Framework 2.0, är generiska i huvudsak en "kodmall" som gör det möjligt för utvecklare att definiera typsäkra datastrukturer utan att binda sig till en faktisk datatyp. Är till exempel List<T> en allmän samling som kan deklareras och användas med valfri typ, till exempel List<int>, List<string>eller List<Person>.

För att förstå varför generiska objekt är användbara ska vi ta en titt på en specifik klass före och efter att vi har lagt till generiska objekt: ArrayList. I .NET Framework 1.0 var elementen ArrayList av typen Object. Alla element som lades till i samlingen konverterades tyst till en Object. Samma sak skulle inträffa när du läser element från listan. Den här processen kallas boxning och avboxning och påverkar prestanda. Förutom prestanda finns det dock inget sätt att fastställa typen av data i listan vid kompileringstid, vilket ger viss bräcklig kod. Generiska objekt löser det här problemet genom att definiera vilken typ av data varje instans av listan ska innehålla. Du kan till exempel bara lägga till heltal i List<int> och bara lägga till Personer i List<Person>.

Generiska program är också tillgängliga vid körning. Körningen vet vilken typ av datastruktur du använder och kan lagra den i minnet mer effektivt.

Följande exempel är ett litet program som illustrerar effektiviteten i att känna till datastrukturtypen vid körning:

  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;

  namespace GenericsExample {
    class Program {
      static void Main(string[] args) {
        //generic list
        List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
        //non-generic list
        ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
        // timer for generic list sort
        Stopwatch s = Stopwatch.StartNew();
        ListGeneric.Sort();
        s.Stop();
        Console.WriteLine($"Generic Sort: {ListGeneric}  \n Time taken: {s.Elapsed.TotalMilliseconds}ms");

        //timer for non-generic list sort
        Stopwatch s2 = Stopwatch.StartNew();
        ListNonGeneric.Sort();
        s2.Stop();
        Console.WriteLine($"Non-Generic Sort: {ListNonGeneric}  \n Time taken: {s2.Elapsed.TotalMilliseconds}ms");
        Console.ReadLine();
      }
    }
  }

Det här programmet genererar utdata som liknar följande:

Generic Sort: System.Collections.Generic.List`1[System.Int32]
 Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
 Time taken: 0.2592ms

Det första du kan märka här är att sortering av den allmänna listan är betydligt snabbare än att sortera den icke-generiska listan. Du kanske också märker att typen för den generiska listan är distinkt ([System.Int32]), medan typen för den icke-generiska listan är generaliserad. Eftersom körningen vet att den generiska List<int> är av typen Int32kan den lagra listelementen i en underliggande heltalsmatris i minnet, medan den icke-generiska ArrayList måste omvandla varje listelement till ett objekt. Som det här exemplet visar tar de extra rollerna upp tid och saktar ned listsortering.

En ytterligare fördel med körningen med vetskapen om vilken typ av generell information som är en bättre felsökningsupplevelse. När du felsöker en allmän i C# vet du vilken typ varje element är i din datastruktur. Utan generiska objekt skulle du inte ha någon aning om vilken typ varje element var.

Se även