refleksiya-nedir-ve-ya-in-reflection-we-trust

Refleksiya nədir və ya “In reflection we trust”

               C# dilini öyrənənlər yəqinki Refleksiya deyilən bir mövzu ilə rastlaşıblar. Yeni başlayanların ən çox çətinlik çəkdiyi mövzulardan biridir. Öyrənilmə çətinlik dərəcəsi 10 üzərindən 5-6 olaraq qiymətləndirmək olar.(Subyektiv fikir) Bu məqaləmizdə refleksiya nə işə yarayır və praktiakda refleksiyanı necə sitifadə edə bilərik suallarına cavab verəcəyik.

Refleksiya nədir?

               Refleksiya proqramın, icra vaxtı(runtime) tipləri müəyyən etməsi və onları müəyyən məqsəd üçün istifadə etməsi prosesidir.

İstifadə etdiyiniz Serializasiya – Deserializasiya,Intellisense,Atributların istifadəsi,sonradan genişlənən proqram modullarının yazılması(later binding) və.s hamısı refleksiyaya əsaslanır. Çox sadə bir fakt deyim: Yəqinki çoxunuz Visual Studio ,Visual Studio Code, Sublime text, Chrome və s. Kimi proqramlara genişlənmələr(extension) yükləmisiniz. Heç düşünmüsünüzmü ki, biz bu proqramların source code-na icazəmiz olmadan necə olur ki, bu proqramlara manipulyasiya edə bilirik? Necə olur ki, visual studio-da yeni menyu əmələ gəlir? Necə olur ki proqramda əvvəldəcən olmayan bir şey sadəcə extension əlavə etməklə proqramda yeni bir funksiya kimi ortaya çıxır? Axı proqramın source code-nu açmadan ona necə yeni funksionallıqlar əlavə ətmək olur? Bax bu sualların cavabı refleksiyada gizlidir.

               Biz C#-da proqram yazarkən adətən tipləri(klas, deleqat, interfeys vəs.) kompilyasiyadan əvvəl müəyyən edirik. Dəqiq olaraq bilirik ki, misalçün bu klasın obyektini yaradıb filan metodunu çağırmaq lazımdır. Yaxud hər hansı bir və ya bir neçə dll-ləri proqrama qoşarkən Add Reference edib dll-ləri proqrama əlavə edirik və onların tiplərini istifadə edirik. Əgər hər hansı bir problem ortaya çıxarsa kompilyator bu haqda bizə məlumat verir.(compile time detection)

               Lakin bir sıra hallar var ki , proqram yazarkən seçilmiş arxitektur genişlənmə üslubundan asılı olaraq bütün proseslər kompilyasiyadan sonra ortaya çıxır. Yəni tiplərin müəyyən olunması, onların obyektinin yaranması və hətta lazımi metodların çağırılması icra vaxtı müəyyənləşir.(runtime detection)

               İşlədiyim şirkətlərdən birində proqram yazılması məhz bu struktura dayanırdı. Deməli bizdə böyük bir proqram var idi, hər dəfə yeni funksionallıq lazım olan vaxtı həmin 10minlərlə sətri olan kodu açıb edit etmirdik.  Sadəcə xırda dll-lər yazırdıq, lazımi qovluğa atırdıq və həmin proqram restart olduqdan sonra həmin dll-ləri oxuyub onların içərisindəki kodları icra edə bilirdi. Bu özü də bir arxitektur yanaşma qaydasıdır. (Şirkətinizdə genişlənə bilən proqram yazılması qaydası başqa ola bilər)

  Refleksiyanı praktikada necə istifadə edək?

Kodları github adresimizdən yükləyərək icra edə bilərsiniz.

Gördüyümüz kimi solution-mızda 3 proyekt var.

ReflectionAppUI adlanan layihə ana proqramımızdır. Məhz bu proqramın irəlidə source- code-larını açmadan onu genişləndirməyə çalışacağıq.

BankofBaku adlanan dll faylımız isə bizim əsas proqrama dinamik şəkildə əlavə ediləcək. Yəni standard dll əlavə edilməsi qaydası (add reference) işlətmədən. Biz razılaşma qaydasına uyğun bir qovluq müəyyənləşdirib yazdığımız dll-ləri həmin qovluğa atacağıq və ana proqram həmin dll-ləri o qovluqdan oxuyub icra edəcək.  

 3-cü proyektimiz isə ProviderProtocol adlanır. Bu proyekt digər iki proyekt üçün körpü rolunu oynayır. Ona görə həm ReflectionAppUİ həm də BankOfBaku proyektimiz özündə ProviderProtocol-u saxlamalıdır.

 Razılaşma qaydası olaraq ReflectionAPPUİ proyektində bin qovluğu ilə eyni səviyyədə libs qovluğu yaradırıq. Həmin qovluqa dll fayllarını atacağıq.

İlk oncə razılaşma qaydamız olan ProviderProtocolu yazaq. Həmin dll çox sadə protokoldan ibarətdir. Bu protokolda provayderin adı və klik olunan zaman görəcəyi işi göstəririk.

namespace ProviderProtocol
{
   public interface IProvider
    {
        string Name { get; set; }

        void OnButtonClicked();
    }
}

İndi isə bu protokolu realizə edən provayder yaradaq. Biz bu layihədə BankofBaku adlı provayder yaradacağıq. Siz isə bu solution-a daha başqa provayderlər əlavə edə bilərsiniz.Yetər ki həmin provayderlər İprovider interfeysin irealizə etsin.

public class BankOfBakuProvider : IProvider
    {
        public string Name { get; set; } = "Bank Of Baku";


        public void OnButtonClicked()
        {
           //implementation simplified for learning..
            MessageBox.Show("This is Bank of Baku provider");
        }
    }

Sonda isə əsas proyektimizə yəni ReflectionAppUI ə gələk. Bu bizim qrafiki interfeysimizdir. Proyektin işləmə qaydası çox bəsitdir. Yazdığımız dll provayderləri atırıq libs qovluğuna. Həmin dl-lər mütləq şəkildə İProvider interfeysini realizə etməlidir. Sonra isə proqramımızda “Reload Providers” düyməsinə klik etməklə provayderlərin ekranda render olunduğunu görürük. Proqram libs qovluğunda olan butun dll-ləri yükləyir, onların İProvider interfeysini realizə edib etmədiyini yoxlayır və əgər həmin interfeysi realizə edibsə ozaman klasın obyektini runtime-da yaradaraq hər provayderə görə bir button əlavə edir interfeysə.

   public partial class MainForm : Form
    {
        ProviderVisualizer _providerVisualizer;
        public MainForm()
        {
            InitializeComponent();
            _providerVisualizer = new ProviderVisualizer(grbx_providers);
        }


        private void MainForm_Load(object sender, EventArgs e)
        {
            //get path to libs folder
            string libsPath = ApplicationPath.PathTo("libs");
            _providerVisualizer.LoadFrom(libsPath);


        }


        private void btn_relaod_Click(object sender, EventArgs e)
        {
            _providerVisualizer.ClearProviders();
            _providerVisualizer.LoadFrom(ApplicationPath.PathTo("libs"));
        }
    }

Proqramın əsas funksionallığı ProviderVisualizer klasında gizlənib.Həmin klasın realizasiyası belədir

namespace ReflectionAppUI.Core
{
   public class ProviderVisualizer
    {
        private readonly Control _control;
        private int _locationX ;
        private int _locationY ;


        public ProviderVisualizer(Control control)
        {
            _control = control;
            InitializeDefaultParams();
        }


        public void ClearProviders()
        {
            _control.Controls.Clear();
            InitializeDefaultParams();
        }


        private void InitializeDefaultParams()
        {
            _locationX = 20;
            _locationY = 34;
        }


        public void AddProvider(IProvider provider)
        {
            Button button = new Button
            {
                Text = provider.Name,
                Size = new Size(150, 100),
                Location = new Point(_locationX, _locationY)
            };
            button.Click += (sndr, args) =>
            {
                provider.OnButtonClicked();
            };
            _locationX += 150;
            _control.Controls.Add(button);
        }


        public void LoadFrom(string path)
        {
            //get path to libs folder
            string libsPath = path;
            //get only dll files
            string[] providers = Directory.GetFiles(libsPath, "*.dll");


            //for simplicity excaped LINQ query...
            //for every provider ....
            foreach (string provider in providers)
            {
                //load it into application RAM..
                Assembly assembly = Assembly.LoadFile(provider);


                //get all types in assembly
                Type[] assemblyTypes = assembly.GetTypes();


                foreach (Type assemblyType in assemblyTypes)
                {


                    Type type = assemblyType.GetInterface("IProvider", true);


                    //if current type implemented IProvider interface then..
                    if (type != null)
                    {
                        //create instance of class at runtime
                        IProvider prvdr = (IProvider)Activator.CreateInstance(assemblyType);


                        this.AddProvider(prvdr);
                    }
                }
            }
        }
    }
}

İndi artıq yazdığımız dll fayllarını libs qovluğuna qoysaq görəcəyik ki, avtomatik yeni button əlavə edildi, əgər dll-i həmin qovluqdan götürüb "Reload Providers" qovluğuna klik etsək ozaman görəcəyik ki, avtomatik olaraq həmin button yoxa çıxır.

Kodları github adresimizdən yükləyərək icra edə bilərsiniz.
Tural

Tural Süleymani

Süleymani Tural Microsoft-un MCSD statuslu mütəxəssisidir, 2008-ci ildən bu yana proqramlaşdırma üzrə tədris aparır

Müəllifin bu dildə ən son postları

Bu yazıları da bəyənə bilərsiniz