Showing posts with label DOTNET. Show all posts
Showing posts with label DOTNET. Show all posts

Tuesday, June 18, 2024

.NET Günlük #1 - Late Binding, Early Binding, const, readonly

C# üzerine yazacağım notları bu blog üzerinde toplamaya karar verdim. Hem teorik anlam da hem de pratik olarak küçük notlar barındıracak bir yazı serisi olacak. Merak ettiğim veya öğrenmek istediğim konular hakkında araştırmalardan öğrendiklerimi bu yazı serisine ekleyeceğim. Tamamen birbirinden bağımsız konular bir yazı içeriğinde toplanabilir. Kesinlikle bir tutorial serisi amacı taşımamaktadır.

Early Binding ve Late Binding

Daha önce duymadığım kavramlar. Early binding derleme sürecinde metot, property ve değişkenlerin belirlenip, doğrulanmasıdır. Derleyici bir nesnenin tipinin ne olduğunu bilir. Early binding durumunda bir hata varsa compile-time yani derleme zamanında hata alınır. Örneklendirmek gerekirse, bir class oluşturduk diyelim. Bu class'tan bir nesne oluşturmuş olalım. Bu nesne üzerinden ben varolmayan bir metot çağırırsam, derleyici böyle bir metodun olmadığına dair derleme anında bir hata verecektir. Bizim buradaki nesnemiz early binding olarak tanımlandı, yani derleyici nesnenin tipi olan class'ı önceden biliyor.

Late binding ise tam tersi yani derleyici, derleme anında object'in tipine dair bir bilgisi olmaz. Örneğin dynamic olarak tanımlanan bir değişken ve buna atanan bir değer olsun. Çalışma anında o değerin tipi ne ise o zaman değişkenin tipi belirlenmiş olur.

Late binding ile alakalı önemli bir husus aslında polimorfizm ile alakalı. Bir base class tanımlamış olalım. Bu base class'ta ise Print adında virtual bir void metot tanımlanmış olsun. Bu base class'tan kalıtım alan bir subclass tanımlayalım ayrıca, yine bu subclass'ta Print adlı metotu tekrar override etmiş olalım. Base class tipinde bir nesne tanımlayıp, buna değer olarak Subclass tipinde bir nesne  değeri atarsak. Bu durumda Print metotlarından hangisinin çağırılacağı çalışma anında yani run-time'da belirlenmiş olur. Bu durumu ise late binding olarak adlandırabiliriz.

const ve readonly

Her ikisi de bir C# keyword'u. İlk başta ikiside aynı işi yapıyor gibi görünebilir ama bariz farklar var. Const keyword'u ile tanımlanan bir constant, declaration esnasında bir değer alır ve compile-time constant olarak daha sonrasında değiştirilmesi mümkün değildir. Readonly'de constant olarak tanımlar değişkeni  ama daha tanımlarken değer atanması zorunlu değildir. Yani kurucu metot üzerinde daha sonrasında readonly constant'ın değeri en başta atanabilir, yine daha sonrasında değiştirilemez. Ancak bu atanma işlemi runtime olarak oluyor const tanımlı bir constant'a nazaran. Tabi en başta deklare ederken değer atanırsa readonly constant'a o zaman atama işlemi compile-time olur.

Ayrıca const tanımlı değişmezlere değer ataması yapılırken atanan değerin bir literal ve sabit bir expression olması gerekir. Readonly'de böyle bir zaruriyet yok ilk değer ataması yapılırken.

Ayrıntılı bilgi içeren kaynaklar:

Friday, November 3, 2023

WPF, MVVM (CommunityTookit.Mvvm) and Dependency Injection

Projelerim için genel olarak bir template oluşturmak için bu yazıyı yazıyorum. Daha sonrasında da kontrol eder ve burayı temel alırım diye düşünüyorum. Bu yazının amacı teorik olarak ilerlemeyecek daha çok pratik ve sonuç odaklı bir şekilde ele alınacaktır. 

Öncelikle .NET 8 üzerinden bir WPF oluşturuldu. Daha sonrasında MVVM mimarisini projeye uygulayacağız. Proje dizininde üç temel klasörü şimdiden oluşturalım:
  • Models
  • ViewModels
  • Views
Views klasöründe TestView adında basit bir şekilde hazırlamış olduğum bir UserControl .xaml dosya oluşturdum:
<UserControl x:Class="LDG_WPF.Views.TestView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:LDG_WPF.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>


            <TextBox Grid.Column="0" Width="200" MaxLength="100">

            </TextBox>
            <Button Grid.Column="1" Content="Export" />
        </Grid>

        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="2*"></ColumnDefinition>
            </Grid.ColumnDefinitions>


            <TextBlock Grid.Column="0"></TextBlock>
        </Grid>
    </Grid>
</UserControl>
 
Şimdi sıra logic katmanı görevi görecek olan ViewModel oluşturmada. Normalde ViewModel'ler için bir base class oluşturup onun üzerinden property değişikliklerini handle etmemizi sağlayan bir yapı kullanılırdı. Ancak bu bir nokta zahmet olmaya başladığı için ve bu gibi işleri bizim yerimize yapan paketler çıktığı için artık bu tarz bir yönelime girmeye gerek yok. Microsoft tarafından geliştirilen CommunityToolkit.Mvvm bizim işimizi fazlasıyla görecektir. Nuget Package Manager üzerinden indirebilirsiniz.  Bu sayede ICommand veya INotifyPropertyChanged gibi interface'leri custom olarak  kendimiz uygulamak zorunda kalmadık. 
public partial class TestViewModel: ObservableObject
{
    [ObservableProperty]
    string? displayText;
    [ObservableProperty]
    string? inputText;

    [RelayCommand]
    public void SetText()
    {
        DisplayText = InputText;
    }
}

Şimdi sıra bu ViewModel'i yazdığımız TestView'e bağlamaya geldi. İlk olarak .xaml dosyasından ViewModel'leri bulabilmek için namespace tanımlamak zorundayız, daha sonrasında bu ViewModel'i View datacontext'ine bağlamak için UserControl.DataContext kullanmaktayız:
<UserControl x:Class="LDG_WPF.Views.TestView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:viewmodels="clr-namespace:LDG_WPF.ViewModels"
             xmlns:local="clr-namespace:LDG_WPF.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <UserControl.DataContext>
        <viewmodels:TestViewModel x:Name="test"/>
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <TextBox Grid.Column="0" Width="200" MaxLength="100" Text="{Binding InputText}">

            </TextBox>
            <Button Grid.Column="1" Content="Export" Command="{Binding SetTextCommand}" />
        </Grid>

        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="2*"></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <TextBlock Grid.Column="0" Text="{Binding DisplayText}"></TextBlock>
        </Grid>
    </Grid>
</UserControl>

Not: Ayrıca ViewModel bulunamadı gibi bir hata alırsanız muhtemelen projeyi rebuild yaparak sorunu ortadan kaldırabilirsiniz.

Test etmek için TestView'i MainView üzerinden çağırmalıyız:

<Window x:Class="LDG_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LDG_WPF"
        xmlns:views="clr-namespace:LDG_WPF.Views"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <views:TestView/>
    </Grid>
</Window>
Textbox'a bir şeyler yazarsak ve export dersek, yazdığımız yazı TextBlock üzerinde görünecektir. Hey hey hey. CommunityToolkit.Mvvm işi bayağı kolaylaştırdı:

Artık sıra geldi bir Dependecy Injection olayını WPF projesi üzerinde uygulamaya. Dependecy Injection'u IoC container teknoloji olan Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.Configuration.Abstractions ve Microsoft.Extensions.Options.ConfigurationExtensions  nuget paketi kullanarak uygulayacağız. App.xaml.cs dosyasında aşağıdaki kodları yazalım:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Configuration;
using System.Data;
using System.Windows;

namespace LDG_WPF
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public IServiceProvider ServiceProvider { get; private set; }

        public  IConfiguration Configuration { get; private set; }

        protected override void OnStartup(StartupEventArgs e)
        {
            var builder = new ConfigurationBuilder();

            Configuration = builder.Build();

            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);

            ServiceProvider = serviceCollection.BuildServiceProvider();
            
            var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
            mainWindow.Show();
        }

        private void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient(typeof(MainWindow));
        }
    }
}
Ayrıca App.xaml dosyasından StartupUri="MainWindow.xaml" opsiyonunu kaldırdım çünkü aynı pencereden tane açılmasına sebep oluyordu:
<Application x:Class="LDG_WPF.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:LDG_WPF"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>