Teknologian jakaminen

WPF/C#: Kuinka toteuttaa riippuvuuden lisäys WPF:ssä

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Esipuhe

Tämä artikkeli käyttää WPF-galleriaprojektia oppiakseen riippuvuuden lisäyksen käsitteet ja kuinka suorittaa riippuvuuslisäys WPF:ssä.

Mikä on riippuvuusinjektio

Dependency Injection (DI) on suunnittelumalli, jota käytetään ohjauksen käänteisperiaatteen (IoC) toteuttamiseen. Riippuvuusinjektion päätarkoitus on siirtää objektien luominen ja objektien välisten riippuvuuksien hallinta objektin sisältä ulkoiseen säilöön tai kehyksiin, mikä parantaa koodin ylläpidettävyyttä, testattavuutta ja joustavuutta.

Riippuvuusruiskeen ydinkäsite

  1. luottaa : Objekti vaatii toisen objektin suorittaakseen työnsä, jolloin edellinen riippuu jälkimmäisestä. Esimerkiksi OrderService-luokka voi luottaa ProductRepository-luokkaan tuotetietojen saamiseksi.
  2. injektio : Välitä riippuvainen objekti sitä tarvitsevalle objektille sen sijaan, että antaisit sitä tarvitsevan objektin luoda itse riippuvaisen objektin. Injektio voidaan saavuttaa rakentajien, ominaisuuksien tai menetelmäparametrien avulla.
  3. kontti : Kehys tai kirjasto, joka hallitsee objektien luomista ja riippuvuuksia. Säiliö vastaa objektien luomisesta, riippuvuuksien ratkaisemisesta ja riippuvien objektien lisäämisestä niitä vaativiin objekteihin.

Riippuvuusruiskeen tyypit

rakentajan ruiskutus: Riippuva objekti välitetään luokan rakentajan kautta.

public class OrderService
{
    private readonly IProductRepository _productRepository;

    public OrderService(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Kiinteistöinjektio: Riippuvat objektit välitetään luokan julkisten ominaisuuksien kautta.

public class OrderService
{
    public IProductRepository ProductRepository { get; set; }
}
  • 1
  • 2
  • 3
  • 4

menetelmä injektio: Riippuvainen objekti välitetään luokan metodiparametrin läpi.

public class OrderService
{
    public void ProcessOrder(IProductRepository productRepository)
    {
        // 使用 productRepository 处理订单
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Miksi riippuvuuspistos on tarpeen

Dependency Injection (DI) on suunnittelumalli, jonka avulla objektien luominen ja objektien välisten riippuvuuksien hallinta voidaan siirtää objektin sisältä ulkoiseen säilöön tai kehykseen. Riippuvuusinjektiolle on useita tärkeitä syitä ja etuja:

  1. Vähennä kytkentää : Riippuvuusinjektio siirtää riippuvuuksien hallinnan objektin sisältä ulkoiseen säilöön, jolloin objektin ei tarvitse osata luoda objekteja, joista se riippuu, vaan sen tarvitsee vain tietää riippuvaisen objektin käyttöliittymä. Tämä voi merkittävästi vähentää objektien välistä kytkentää ja tehdä koodista modulaarisemman ja joustavamman.
  2. Paranna testattavuutta : Riippuvuusinjektio tekee yksikkötestauksesta helpompaa ja tehokkaampaa. Käyttämällä valeobjekteja tai tyngöitä todellisten riippuvaisten objektien sijasta kehittäjät voivat suorittaa yksikkötestauksen luottamatta todelliseen toteutukseen. Tämä auttaa varmistamaan testin riippumattomuuden ja luotettavuuden.
  3. Paranna ylläpidettävyyttä : Koska riippuvuuden lisäys vähentää objektien välistä kytkentää, koodista tulee modulaarisempi ja selkeämpi. Tämä tekee koodista helpompi ymmärtää ja ylläpitää. Kun sinun on muutettava tai korvattava riippuvainen objekti, sinun tarvitsee vain muokata kokoonpano- tai rekisteröintitietoja muuttamatta objektia käyttävää koodia.
  4. Parantaa joustavuutta : Riippuvuuden lisäys tekee järjestelmästä joustavamman ja voi helposti korvata riippuvaisia ​​objekteja erilaisten toimintojen tai käyttäytymisen saavuttamiseksi. Esimerkiksi erilaisia ​​tietokannan pääsykerroksen toteutuksia voidaan vaihtaa konfigurointitiedostojen tai koodin avulla muuttamatta liiketoimintalogiikkakoodia.
  5. Edistä huolenaiheiden erottamista : Riippuvuusinjektio auttaa saavuttamaan huolenaiheiden erottamisen (Separation of Concerns), niin että jokaisen kohteen tarvitsee keskittyä vain omiin velvollisuuksiinsa välittämättä siitä, kuinka luodaan ja hankitaan esineitä, joista se riippuu. Tämä auttaa parantamaan koodin selkeyttä ja ylläpidettävyyttä.
  6. Tukea suunnittelumalleja ja parhaita käytäntöjä : Riippuvuuden lisäys on perusta monille suunnittelumalleille ja parhaille käytännöille, kuten käänteinen ohjaus (IoC), Service Locator Pattern jne. Käyttämällä riippuvuuden lisäystä kehittäjät voivat helpommin ottaa nämä mallit ja käytännöt käyttöön, mikä parantaa koodin laatua ja skaalautuvuutta.

Kuinka toteuttaa riippuvuusruiske

Tämä artikkeli käyttää WPF-galleriaprojektia oppiakseen käyttämään riippuvuuslisäystä WPF-koodiosoitteessa:

https://github.com/microsoft/WPF-Samples/blob/main/SampleApplications/WPFGallery

Riippuvuuslisäyksen toteuttamiseksi tässä projektissa käytetään näitä kahta pakettia:

kuva-20240711100435001

Katso ensin App.xaml.cs:n sisältö:

public partial class App : Application
{

    private static readonly IHost _host = Host.CreateDefaultBuilder()
        .ConfigureServices((context, services) =>
        {
            services.AddSingleton<INavigationService, NavigationService>();
            services.AddSingleton<MainWindow>();
            services.AddSingleton<MainWindowViewModel>();
            
            services.AddTransient<DashboardPage>();
            services.AddTransient<DashboardPageViewModel>();

            services.AddTransient<ButtonPage>();
            services.AddTransient<ButtonPageViewModel>();
            services.AddTransient<CheckBoxPage>();
            services.AddTransient<CheckBoxPageViewModel>();
            services.AddTransient<ComboBoxPage>();
            services.AddTransient<ComboBoxPageViewModel>();
            services.AddTransient<RadioButtonPage>();
            services.AddTransient<RadioButtonPageViewModel>();
            services.AddTransient<SliderPage>();
            services.AddTransient<SliderPageViewModel>();
            services.AddTransient<CalendarPage>();
            services.AddTransient<CalendarPageViewModel>();
            services.AddTransient<DatePickerPage>();
            services.AddTransient<DatePickerPageViewModel>();
            services.AddTransient<TabControlPage>();
            services.AddTransient<TabControlPageViewModel>();
            services.AddTransient<ProgressBarPage>();
            services.AddTransient<ProgressBarPageViewModel>();
            services.AddTransient<MenuPage>();
            services.AddTransient<MenuPageViewModel>();
            services.AddTransient<ToolTipPage>();
            services.AddTransient<ToolTipPageViewModel>();
            services.AddTransient<CanvasPage>();
            services.AddTransient<CanvasPageViewModel>();
            services.AddTransient<ExpanderPage>();
            services.AddTransient<ExpanderPageViewModel>();
            services.AddTransient<ImagePage>();
            services.AddTransient<ImagePageViewModel>();
            services.AddTransient<DataGridPage>();
            services.AddTransient<DataGridPageViewModel>();
            services.AddTransient<ListBoxPage>();
            services.AddTransient<ListBoxPageViewModel>();
            services.AddTransient<ListViewPage>();
            services.AddTransient<ListViewPageViewModel>();
            services.AddTransient<TreeViewPage>();
            services.AddTransient<TreeViewPageViewModel>();
            services.AddTransient<LabelPage>();
            services.AddTransient<LabelPageViewModel>();
            services.AddTransient<TextBoxPage>();
            services.AddTransient<TextBoxPageViewModel>();
            services.AddTransient<TextBlockPage>();
            services.AddTransient<TextBlockPageViewModel>();
            services.AddTransient<RichTextEditPage>();
            services.AddTransient<RichTextEditPageViewModel>();
            services.AddTransient<PasswordBoxPage>();
            services.AddTransient<PasswordBoxPageViewModel>();
            services.AddTransient<ColorsPage>();
            services.AddTransient<ColorsPageViewModel>();

            services.AddTransient<LayoutPage>();
            services.AddTransient<LayoutPageViewModel>();
            services.AddTransient<AllSamplesPage>();
            services.AddTransient<AllSamplesPageViewModel>();
            services.AddTransient<BasicInputPage>();
            services.AddTransient<BasicInputPageViewModel>();
            services.AddTransient<CollectionsPage>();
            services.AddTransient<CollectionsPageViewModel>();
            services.AddTransient<MediaPage>();
            services.AddTransient<MediaPageViewModel>();
            services.AddTransient<NavigationPage>();
            services.AddTransient<NavigationPageViewModel>();
            services.AddTransient<TextPage>();
            services.AddTransient<TextPageViewModel>();
            services.AddTransient<DateAndTimePage>();
            services.AddTransient<DateAndTimePageViewModel>();
            services.AddTransient<StatusAndInfoPage>();
            services.AddTransient<StatusAndInfoPageViewModel>();
            services.AddTransient<SamplesPage>();
            services.AddTransient<SamplesPageViewModel>();
            services.AddTransient<DesignGuidancePage>();
            services.AddTransient<DesignGuidancePageViewModel>();

            services.AddTransient<UserDashboardPage>();
            services.AddTransient<UserDashboardPageViewModel>();

            services.AddTransient<TypographyPage>();
            services.AddTransient<TypographyPageViewModel>();

            services.AddSingleton<IconsPage>();
            services.AddSingleton<IconsPageViewModel>();

            services.AddSingleton<SettingsPage>();
            services.AddSingleton<SettingsPageViewModel>();

            services.AddSingleton<AboutPage>();
            services.AddSingleton<AboutPageViewModel>();
        }).Build();


    [STAThread]
    public static void Main()
    {
        _host.Start();

        App app = new();
        app.InitializeComponent();
        app.MainWindow = _host.Services.GetRequiredService<MainWindow>();
        app.MainWindow.Visibility = Visibility.Visible;
        app.Run();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

kuva-20240711083011393

Mikä on IHost?

C#:ssa,IHost on käyttöliittymä, jota käytetään .NET:ssä sovellusten rakentamiseen ja konfigurointiinHostkäsite abstrakti.IHost Käyttöliittymä määrittelee joukon palveluita ja komponentteja, joita tarvitaan sovelluksen käynnistämiseen, suorittamiseen ja hallintaan. Sitä käytetään yleensä ASP.NET Core -sovellusten kanssa, mutta se toimii myös muun tyyppisten .NET-sovellusten, kuten konsolisovellusten tai WPF-ohjelmien, kanssa.

kuva-20240711082817268

IHostKäyttöliittymä koostuuHostBuilderLuokan toteutus, joka tarjoaa luomisen ja konfiguroinninIHostesimerkkimenetelmiä.HostBuilderVoit lisätä erilaisia ​​palveluita, kuten kirjaamisen, määrityksen, riippuvuuden lisäyssäiliöt jne., ja määrittää sovelluksesi käynnistys- ja pysäytyskäyttäytymisen.

kuva-20240711083048854

kuva-20240711083156306

Tarjoaa käteviä menetelmiä Microsoft.Extensions.Hosting.IHostBuilder-esiintymien luomiseen ennalta määritetyillä oletusarvoilla.

kuva-20240711083713204

Palauttaa IHostBuilderin.

kuva-20240711084145756

kuva-20240711084211035

Lisää palveluita säilöön. Tätä toimintoa voidaan kutsua useita kertoja ja sen tulokset ovat kumulatiivisia.

Parametrin configureDelegate tarkoitus on määrittää Microsoft.Extensions.DependencyInjection.IServiceCollectionin delegaatti.
Tätä kokoelmaa käytetään System.IServiceProviderin rakentamiseen.

Valtuutettu vaatii kaksi parametrityyppiä: HostBuilderContext ja IServiceCollection, joilla ei ole palautusarvoa.

kuva-20240711084853971

Lambda-lauseke, joka täyttää delegaatin tyypin, välitetään tähän.

C#:ssa,() => {} On syntaksi lambda-lausekkeille. Lambda-lausekkeet ovat kevyt delegaatin kääre, jonka avulla voit määrittää anonyymin menetelmän ja välittää sen parametrina menetelmälle, joka tukee delegaatteja tai lausekepuita.

Lambda-lausekkeet tarjoavat tiiviin tavan määrittää menetelmiä, ja ne ovat hyödyllisiä erityisesti silloin, kun menetelmiä on välitettävä parametreina muille menetelmille.

kuva-20240711085344696

Palveluita lisättäessä tässä on kaksi elinkaaria, AddSingletonin ja AddTransientin lisäksi mukana on myös AddScoped.

Nämä menetelmät määrittävät palvelun elinkaaren eli kuinka palveluesiintymiä luodaan ja hallitaan sovelluksessa.

AddSingleton

  • elinkaari: Singleton
  • merkitys : Vain yksi palveluesiintymä luodaan koko sovelluksen elinkaaren aikana. Riippumatta siitä, kuinka monta kertaa pyydät sitä säilöstä, sama ilmentymä palautetaan.
  • Sovellettava kohtaus: Soveltuu tilattomille palveluille tai koko sovellukselle jaetuille resursseille, kuten kokoonpanoille, lokereille jne.

AddTransient

  • elinkaari: ohimenevä
  • merkitys: Aina kun palvelua pyydetään säilöltä, luodaan uusi ilmentymä.
  • Sovellettava kohtaus: Sopii tilallisiin palveluihin tai skenaarioihin, jotka vaativat uuden esiintymän jokaiselle pyynnölle, kuten sivuille, näkymämalleille jne.

AddScoped

  • elinkaari: Tarkoitettu
  • merkitys : Palveluesiintymät ovat jokaisessa laajuudessa ainutlaatuisia. Soveltamisalat liitetään usein pyynnön elinkaareen, esimerkiksi verkkosovelluksissa jokaisella HTTP-pyynnöllä luodaan uusi soveltamisala.
  • Sovellettava kohtaus: Sopii palveluille, joiden on jaettava ilmentymiä pyyntöalueen sisällä, kuten tietokantakonteksteissa.

käyttää näitä palveluita

Päätoiminnossa:

kuva-20240711095100016

aloittaa_host,kulkea_host.Services.GetRequiredService<MainWindow>();Hanki MainWindow-instanssi.

Esimerkkinä MainWindow-luokan katsotaan MainWindow-konstruktoria MainWindow.xaml.cs-tiedostossa:

public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
{
    _serviceProvider = serviceProvider;
    ViewModel = viewModel;
    DataContext = this;
    InitializeComponent();

    Toggle_TitleButtonVisibility();

    _navigationService = navigationService;
    _navigationService.Navigating += OnNavigating;
    _navigationService.SetFrame(this.RootContentFrame);
    _navigationService.Navigate(typeof(DashboardPage));

    WindowChrome.SetWindowChrome(
        this,
        new WindowChrome
        {
            CaptionHeight = 50,
            CornerRadius = default,
            GlassFrameThickness = new Thickness(-1),
            ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4),
            UseAeroCaptionButtons = true
        }
    );

    this.StateChanged += MainWindow_StateChanged;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Aiheeseen liittymättömän sisällön poistamisen jälkeen tilanne on seuraava:

public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
{
    _serviceProvider = serviceProvider;
    ViewModel = viewModel; 
    _navigationService = navigationService;  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Oletko huomannut, että näitä objekteja ei enää tarvitse itse luoda.

Jos riippuvuusinjektiota ei käytetä, se voi näyttää tältä:

public MainWindow()
{
    _serviceProvider = new IServiceProvider();
    ViewModel = new MainWindowViewModel(); 
    _navigationService = new INavigationService();  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Tee yhteenveto

Tässä artikkelissa esitellään ensin riippuvuuslisäyksen käsite, sitten selitetään, miksi riippuvuuslisäystä tarvitaan, ja lopuksi opitaan käyttämään riippuvuuden lisäystä WPF:ssä WPF Gallery -projektin kautta.

viitata

1、[WPF-Samples/Sample Applications/WPFGallery pääsivulla · microsoft/WPF-Samples (github.com)](https://github.com/microsoft/WPF-Samples/tree/main/Sample Applications/WPFGallery)