Programmering

Hvordan bruke inversjon av kontroll i C #

Både inversjon av kontroll og avhengighetsinjeksjon gjør at du kan bryte avhengighet mellom komponentene i applikasjonen og gjøre applikasjonen lettere å teste og vedlikeholde. Imidlertid er inversjon av kontroll og avhengighetsinjeksjon ikke den samme - det er subtile forskjeller mellom de to.

I denne artikkelen vil vi undersøke inversjonen av kontrollmønsteret og forstå hvordan det skiller seg fra avhengighetsinjeksjon med relevante kodeeksempler i C #.

For å jobbe med kodeeksemplene som er gitt i denne artikkelen, bør du ha Visual Studio 2019 installert på systemet ditt. Hvis du ikke allerede har en kopi, kan du laste ned Visual Studio 2019 her.

Opprett et konsollapplikasjonsprosjekt i Visual Studio

La oss først lage et .NET Core-konsollapplikasjonsprosjekt i Visual Studio. Forutsatt at Visual Studio 2019 er installert i systemet ditt, følger du trinnene som er beskrevet nedenfor for å opprette et nytt .NET Core-konsollapplikasjonsprosjekt i Visual Studio.

  1. Start Visual Studio IDE.
  2. Klikk på "Opprett nytt prosjekt."
  3. I vinduet "Opprett nytt prosjekt" velger du "Konsollapp (.NET Core)" fra listen over maler som vises.
  4. Klikk på Neste.
  5. I vinduet “Konfigurer ditt nye prosjekt” som vises, angir du navnet og stedet for det nye prosjektet.
  6. Klikk på Opprett.

Dette vil opprette et nytt .NET Core-konsollapplikasjonsprosjekt i Visual Studio 2019. Vi bruker dette prosjektet til å utforske inversjon av kontroll i de påfølgende delene av denne artikkelen.

Hva er inversjon av kontroll?

Inversjon av kontroll (IoC) er et designmønster der kontrollflyten til et program er invertert. Du kan dra nytte av inversjonen av kontrollmønsteret for å koble komponentene i applikasjonen din, bytte avhengighetsimplementeringer, spotte avhengigheter, og gjøre applikasjonen modulær og testbar.

Avhengighetsinjeksjon er en delmengde av inversjonen av kontrollprinsippet. Med andre ord er avhengighetsinjeksjon bare en måte å implementere inversjon av kontroll på. Du kan også implementere inversjon av kontroll ved hjelp av hendelser, delegater, malmønster, fabrikkmetode eller tjenestesøk, for eksempel.

Inversjonen av kontrolldesignmønsteret sier at objekter ikke skal lage objekter som de er avhengige av for å utføre noen aktivitet. I stedet bør de få disse gjenstandene fra en ekstern tjeneste eller en container. Ideen er analog med Hollywood-prinsippet som sier: "Ikke ring oss, vi vil ringe deg." Som et eksempel, i stedet for at applikasjonen kaller metodene i et rammeverk, vil rammeverket kalle implementeringen som er gitt av applikasjonen.

Inversjon av kontrolleksempel i C #

Anta at du bygger en ordrebehandlingsapplikasjon og at du vil implementere logging. For enkelhets skyld, la oss anta at loggmålet er en tekstfil. Velg konsollapplikasjonsprosjektet du nettopp opprettet i Solution Explorer-vinduet, og opprett to filer, kalt ProductService.cs og FileLogger.cs.

  offentlig klasse ProductService

    {

privat readonly FileLogger _fileLogger = ny FileLogger ();

offentlig ugyldig logg (strengmelding)

        {

_fileLogger.Log (melding);

        }

    }

offentlig klasse FileLogger

    {

offentlig ugyldig logg (strengmelding)

        {

Console.WriteLine ("Inside Log-metoden for FileLogger.");

LogToFile (melding);

        }

privat ugyldig LogToFile (strengmelding)

        {

Console.WriteLine ("Metode: LogToFile, tekst: {0}", melding);

        }

    }

Implementeringen vist i forrige kodebit er riktig, men det er en begrensning. Du er begrenset til å bare logge data til en tekstfil. Du kan ikke på noen måte logge data til andre datakilder eller forskjellige loggmål.

En ufleksibel implementering av loggføring

Hva om du ville logge data til en databasetabell? Den eksisterende implementeringen støtter ikke dette, og du vil bli tvunget til å endre implementeringen. Du kan endre implementeringen av FileLogger-klassen, eller du kan opprette en ny klasse, for eksempel DatabaseLogger.

    offentlig klasse DatabaseLogger

    {

offentlig ugyldig logg (strengmelding)

        {

Console.WriteLine ("Inside Log-metoden til DatabaseLogger.");

LogToDatabase (melding);

        }

privat ugyldig LogToDatabase (strengmelding)

        {

Console.WriteLine ("Metode: LogToDatabase, tekst: {0}", melding);

        }

    }

Du kan til og med opprette en forekomst av DatabaseLogger-klassen i ProductService-klassen som vist i kodebiten nedenfor.

offentlig klasse ProductService

    {

privat readonly FileLogger _fileLogger = ny FileLogger ();

privat readonly DatabaseLogger _databaseLogger =

ny DatabaseLogger ();

offentlig ugyldig LogToFile (strengmelding)

        {

_fileLogger.Log (melding);

        }

offentlig ugyldig LogToDatabase (strengmelding)

        {

_fileLogger.Log (melding);

        }

    }

Men selv om dette ville fungere, hva om du trengte å logge applikasjonens data til EventLog? Designet ditt er ikke fleksibelt, og du vil bli tvunget til å endre ProductService-klassen hver gang du trenger å logge på et nytt loggmål. Dette er ikke bare tungvint, men vil også gjøre det ekstremt vanskelig for deg å administrere ProductService-klassen over tid.

Legg til fleksibilitet med et grensesnitt

Løsningen på dette problemet er å bruke et grensesnitt som betongloggerklassene vil implementere. Følgende kodebit viser et grensesnitt som heter ILogger. Dette grensesnittet vil bli implementert av de to konkrete klassene FileLogger og DatabaseLogger.

offentlig grensesnitt ILogger

{

ugyldig Logg (strengmelding);

}

De oppdaterte versjonene av klassene FileLogger og DatabaseLogger er gitt nedenfor.

offentlig klasse FileLogger: ILogger

    {

offentlig ugyldig logg (strengmelding)

        {

Console.WriteLine ("Inside Log-metoden for FileLogger.");

LogToFile (melding);

        }

privat ugyldig LogToFile (strengmelding)

        {

Console.WriteLine ("Metode: LogToFile, tekst: {0}", melding);

        }

    }

offentlig klasse DatabaseLogger: ILogger

    {

offentlig ugyldig logg (strengmelding)

        {

Console.WriteLine ("Inside Log-metoden til DatabaseLogger.");

LogToDatabase (melding);

        }

privat ugyldig LogToDatabase (strengmelding)

        {

Console.WriteLine ("Metode: LogToDatabase, tekst: {0}", melding);

        }

    }

Du kan nå bruke eller endre den konkrete implementeringen av ILogger-grensesnittet når det er nødvendig. Følgende kodebit viser ProductService-klassen med en implementering av Logg-metoden.

offentlig klasse ProductService

    {

offentlig ugyldig logg (strengmelding)

        {

ILogger logger = ny FileLogger ();

logger.Log (melding);

        }

    }

Så langt så bra. Men hva om du vil bruke DatabaseLogger i stedet for FileLogger i Logg-metoden i ProductService-klassen? Du kan endre implementeringen av Logg-metoden i ProductService-klassen for å oppfylle kravet, men det gjør ikke designet fleksibelt. La oss nå gjøre designen mer fleksibel ved å bruke inversjon av kontroll og avhengighetsinjeksjon.

Inverter kontrollen ved hjelp av avhengighetsinjeksjon

Følgende kodebit illustrerer hvordan du kan dra nytte av avhengighetsinjeksjon for å passere en forekomst av en betongloggerklasse ved hjelp av konstruktørinjeksjon.

offentlig klasse ProductService

    {

privat readonly ILogger _logger;

offentlig ProductService (ILogger logger)

        {

_logger = logger;

        }

offentlig ugyldig logg (strengmelding)

        {

_logger.Log (melding);

        }

    }

Til slutt, la oss se hvordan vi kan overføre en implementering av ILogger-grensesnittet til ProductService-klassen. Følgende kodebit viser hvordan du kan lage en forekomst av FileLogger-klassen og bruke konstruktørinjeksjon for å passere avhengigheten.

statisk tomrom Main (streng [] args)

{

ILogger logger = ny FileLogger ();

ProductService productService = ny ProductService (logger);

productService.Log ("Hello World!");

}

Ved å gjøre det har vi omvendt kontrollen. ProductService-klassen er ikke lenger ansvarlig for å lage en forekomst av en implementering av ILogger-grensesnittet eller til og med bestemme hvilken implementering av ILogger-grensesnittet som skal brukes.

Inversjon av kontroll og avhengighetsinjeksjon hjelper deg med automatisk instantiering og livssyklusadministrasjon av objektene dine. ASP.NET Core inkluderer en enkel, innebygd inversjon av kontrollcontainer med et begrenset sett med funksjoner. Du kan bruke denne innebygde IoC-containeren hvis dine behov er enkle, eller bruke en tredjepartscontainer hvis du vil utnytte flere funksjoner.

Du kan lese mer om hvordan du arbeider med inversjon av kontroll og avhengighetsinjeksjon i ASP.NET Core i mitt tidligere innlegg her.

$config[zx-auto] not found$config[zx-overlay] not found