CodeGym /Kurslar /C# SELF /Extension Members: metodlar

Extension Members: metodlar

C# SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Obyekt-yönümlü proqramlaşdırma dünyasında biz tez-tez mövcud siniflərə yeni funksionallıq əlavə etməyə ehtiyac duyuruq. Bəzən həmin siniflərin mənbə koduna çıxışımız olmur (məsələn, bunlar standart .NET tipləri, üçüncü tərəf kitabxanalarından olan siniflər və ya generasiya olunmuş kod ola bilər).

Belə hallarda köməyə Extension Methods — C# dilinin güclü və zərif xüsusiyyəti gəlir, hansı ki, mövcud tiplərə "saxta" şəkildə yeni metodlar əlavə etməyə imkan verir, onları birbaşa dəyişmədən.

Extension methods — C#-da sintaktik şəkərdir, hansı ki, sənə statik metodları elə çağırmağa imkan verir ki, guya onlar tipin instans metodlarıdır. Onlar elə görünür və istifadə olunur ki, guya həmişə genişləndirilən tipin bir hissəsi olublar, amma əslində belə deyil.

Tutaq ki, sənə bir metod lazımdır ki, sətrin ilk hərfini böyük hərfə çevirsin:


public static class StringUtil
{
    public static string CapitalizeFirstLetter(string str)
    {
        if (string.IsNullOrEmpty(str)) return str;
        return char.ToUpper(str[0]) + str.Substring(1);
    }
}

// istifadə
string hello = StringUtil.CapitalizeFirstLetter("salam"); // "Salam"
Console.WriteLine(hello); 

Kod, koddu, amma C# bunu bir neçə dəfə daha qısa yazmağa imkan verir.

Bu sətri

string hello = StringUtil.CapitalizeFirstLetter("salam"); // "Salam"

belə də yaza bilərsən:

string hello = "salam".CapitalizeFirstLetter(); // "Salam"

İndi isə izah edəcəm bu necə işləyir.

2. Extension Methods necə qurulub

Extension Methods bir az sehr kimi görünsə də, əslində olduqca sadədir. Extension method — adi statik metoddur, statik sinifdə elan olunur, amma bir vacib fərqlə: onun ilk parametri this açar sözü ilə işarələnir. Məhz bu parametr göstərir ki, həmin metod hansı tipə "genişlənmə" kimi tətbiq olunur.

Gəlin string tipini genişləndirməyə nümunəyə baxaq:

namespace MyProject.Extensions
{
    // 1. Extension Methods mütləq statik sinifdə elan olunmalıdır.
    public static class StringExtensions
    {
        // 2. Metod statik olmalıdır.
        // 3. Metodun ilk parametri 'this' açar sözü ilə işarələnməlidir.
        public static string CapitalizeFirstLetter(this string str)
        {
            if (string.IsNullOrEmpty(str)) 
                return str; 

            // Yeni sətr yaratmaq üçün String.ToUpper və String.Substring metodlarından istifadə edirik
            return char.ToUpper(str[0]) + str.Substring(1);
        }

        // Başqa bir nümunə də əlavə edək:
        public static int WordCount(this string text)
        {
            if (string.IsNullOrEmpty(text))
                return 0;

            // Ən sadə söz sayımı, boşluqlara görə bölürük
            return text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }
}    

İndi bu metodlardan istifadə etmək üçün sənə sadəcə MyProject.Extensions namespace-ni faylında əlavə etmək lazımdır:

using MyProject.Extensions; // Vacibdir: Extension Methods harada elan olunubsa, həmin namespace-i import et

string hello = "salam, dünya!";
Console.WriteLine(hello.CapitalizeFirstLetter()); // Çap edəcək: "Salam, dünya!"

string sentence = "Bu sözlərin sayını yoxlamaq üçün test sətridir.";
Console.WriteLine($"Sözlərin sayı: {sentence.WordCount()}"); // Çap edəcək: "Sözlərin sayı: 6"

string emptyString = "";
Console.WriteLine(emptyString.CapitalizeFirstLetter()); // Çap edəcək: ""

Vacibdir: Əgər this açar sözü ilk parametrin (string str) qarşısında olmasaydı, CapitalizeFirstLetter sadəcə StringExtensions sinifinin adi statik metodu olardı və onu belə çağırmalı idin: StringExtensions.CapitalizeFirstLetter("salam"). Məhz this onu Extension Method-a çevirir və obyekt-yönümlü stil ilə çağırmağa imkan verir.

3. Extension Methods-un sintaksisi

Extension Methods yaratmaq və istifadə etmək üçün bu addımları izlə:

  1. Statik sinif yarat: Bütün Extension Methods statik sinifdə elan olunmalıdır. Sinifin adı istənilən ola bilər, amma adətən [TypeName]Extensions (məsələn, StringExtensions, ListExtensions, DateTimeExtensions) adlandırmaq yaxşı praktikadır.
  2. Statik metod elan et: Statik sinifinin içində adi statik metod yaz.
  3. İlk parametri this ilə işarələ: Ən vacibi — statik metodunun ilk parametri this açar sözü ilə başlamalıdır, sonra isə genişləndirmək istədiyin tip gəlir. Bu parametr həmin obyektin instansını təmsil edir, hansı üçün Extension Method çağırılır.
  4. Namespace-i əlavə et: Extension Method istifadə etmək istədiyin faylda, həmin statik sinifin olduğu namespace-i (using) əlavə et. Onsuz kompilyator sənin extension-ları tapa bilməyəcək.
  5. Adi metod kimi istifadə et: İndi Extension Method-u elə çağırırsan ki, guya o, həmin tipin instans metodudur.

4. "Sehr" əslində necə işləyir

Extension Methods-un "sehrini" C# kompilyatoru edir. Proqram işləyərkən Extension Methods sinifin metodlarının bir hissəsi olmur. Əvəzində, C# kompilyatoru Extension Method çağırışını adi statik çağırışa çevirir.

Sən obj.Method(args) yazanda, burada Method Extension Method-dursa, kompilyator proqramı yığanda bunu əslində ContainingClass.Method(obj, args) şəklində çevirir. Bu, proqramçı üçün görünməz baş verir və elə təəssürat yaradır ki, guya metod sinifin özüdür.

Konfliktlərin həlli: Extension Methods vs Adi Metodlar

Bəs nə baş verər, əgər sinifdə artıq eyni ad və imzaya (parametrlərin sayı və tipi) malik "native" (instans) metod varsa və sənin Extension Method-un da eyni ad və imzaya malikdirsə?

public class MyClass
{
    public void DoSomething(int value)
    {
        Console.WriteLine($"Native metod MyClass.DoSomething: {value}");
    }
}

public static class MyClassExtensions
{
    public static void DoSomething(this MyClass instance, int value)
    {
        Console.WriteLine($"Extension Method MyClassExtensions.DoSomething: {value}");
    }
}

// İstifadə:
MyClass obj = new MyClass();
obj.DoSomething(10); // Native metod MyClass.DoSomething: 10 çağırılacaq

Prioritet qaydası: Əgər sinifdə artıq eyni ad və imzaya malik instans metodu varsa, həmişə native instans metodu çağırılacaq, Extension Method yox. Extension Methods ad konfliktində daha aşağı prioritetə malikdir.

Extension Methods-un məhdudiyyətləri

  • Mövcud metodları override etmək olmaz: Extension Methods polimorfizmdə iştirak etmir.
  • Yeni field, property və ya event əlavə etmək olmaz: Yalnız metod əlavə edə bilərsən. Onlarla tipə yeni vəziyyət (state) əlavə etmək mümkün deyil.
  • Statik sinifləri genişləndirmək olmaz: İlk parametr this həmişə tipin instansı (obyekti) olmalıdır.
  • Field və ya property genişləndirmək olmaz: Yalnız tipləri genişləndirmək olar.
  • Private və ya protected üzvlərə çıxış yoxdur: Extension Methods yalnız public üzvlərlə işləyə bilər.

Amma sən Extension Methods-u əlavə edə bilərsən:

  • İnterfeyslərə: Bu çox güclü imkandır! Sən müəyyən interfeysi implement edən bütün siniflərə metod əlavə edə bilərsən, interfeysin və ya hər sinifin özünü dəyişmədən. Məsələn, IEnumerable<T> implement edən bütün siniflər LINQ-dan Extension Methods "alır".
  • object tipinə: Bu, C#-da istənilən obyekt üçün Extension Methods əlçatan edir. Amma bunu çox ehtiyatla istifadə et, yoxsa global namespace-i çirkləndirə və ad konfliktləri yarada bilərsən.

5. Real nümunələr və Best Practices

Extension Methods — sadəcə nəzəri konsept deyil, gündəlik proqramlaşdırmada güclü alətdir. Budur bir neçə praktik ssenari:

1. DateTime-ı genişləndirmək:

Biznes məntiqində tez-tez istifadə olunan tarixlərlə işləmək üçün rahat metodlar əlavə etmək.

using System;

namespace MyProject.TimeHelpers
{
    public static class DateTimeExtensions
    {
        public static bool IsWeekend(this DateTime date)
        {
            return date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
        }

        public static bool IsWeekday(this DateTime date)
        {
            return !date.IsWeekend(); // Artıq mövcud Extension Method-dan istifadə edirik!
        }

        public static DateTime NextDay(this DateTime date)
        {
            return date.AddDays(1);
        }

        public static DateTime AddBusinessDays(this DateTime date, int days)
        {
            DateTime newDate = date;
            while (days > 0)
            {
                newDate = newDate.NextDay();
                if (newDate.IsWeekday())
                {
                    days--;
                }
            }
            return newDate;
        }
    }
}

İstifadə:

using MyProject.TimeHelpers;

DateTime today = DateTime.Now;
if (today.IsWeekend())
{
    Console.WriteLine("Bu gün istirahət günüdür!");
}
else
{
    Console.WriteLine("Bu gün iş günüdür.");
}

DateTime tomorrow = today.NextDay();
Console.WriteLine($"Sabah: {tomorrow.ToShortDateString()}");

DateTime futureDate = today.AddBusinessDays(5);
Console.WriteLine($"5 iş günündən sonra olacaq: {futureDate.ToShortDateString()}");

2. Öz tipini genişləndirmək (məsələn, Dog):

Hətta sinifin mənbə koduna çıxışın olsa belə, Extension Methods kömək edə bilər ki, köməkçi məntiqi ayırasan və ya Single Responsibility Principle-ə əməl edəsən.

namespace MyProject.Animals
{
    public class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Breed { get; set; }
    }

    public static class DogExtensions
    {
        public static bool IsPuppy(this Dog dog)
        {
            // Şərti bala təyini
            return dog.Age < 2;
        }

        public static string GetAgeCategory(this Dog dog)
        {
            if (dog.Age < 1) return "Bala";
            if (dog.Age < 7) return "Böyük it";
            return "Qoca it";
        }

        public static void Bark(this Dog dog)
        {
            Console.WriteLine($"{dog.Name} deyir: Hav! Hav!");
        }
    }
}

İstifadə:


using MyProject.Animals;

var sharik = new Dog { Name = "Şarik", Age = 1, Breed = "Qarışıq" };
if (sharik.IsPuppy())
{
    Console.WriteLine($"{sharik.Name} — hələ baladır!"); // Şarik — hələ baladır!
}

Console.WriteLine($"{sharik.Name} kateqoriyada: {sharik.GetAgeCategory()}"); // Şarik kateqoriyada: Bala
sharik.Bark(); // Şarik deyir: Hav! Hav!

var rex = new Dog { Name = "Reks", Age = 5, Breed = "Çoban iti" };
Console.WriteLine($"{rex.Name} kateqoriyada: {rex.GetAgeCategory()}"); // Reks kateqoriyada: Böyük it

Best Practices:

Məna dolu adlar seç: Extension siniflərinin (StringExtensions, ListExtensions) və metodların adları aydın və funksionallığı əks etdirən olmalıdır.

Məntiqi namespace-lərdə saxla: Extension Methods-u məqsədinə uyğun namespace-lərdə qruplaşdır, global namespace-i yükləmə.

Ağılla istifadə et: Extension Methods-u həddindən artıq istifadə etmə. Əgər sinifin mənbə koduna çıxışın varsa və metodu birbaşa əlavə edə bilirsənsə, bəlkə də bu daha təmiz həll olar. Extension Methods ən yaxşı halda intruziv olmayan funksionallıq əlavə etmək üçündür.

Ad konfliktlərindən qaç: Native metodların prioritetini unutma. Əgər Extension Method-un eyni ad və imzaya malikdirsə, o çağırılmayacaq.

Metodun təmizliyi: Extension Methods konkret, fokuslanmış işi görməlidir. Onları çox mürəkkəb etmə.

Test oluna bilmə: Extension Methods — adi statik metodlardır, bu da onları test etməyi çox asanlaşdırır.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Sətir üçün sadə Extension Method yaradılması
Sətir üçün sadə Extension Method yaradılması
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
int tipində ədədlərlə işləmək üçün genişləndirmə
int tipində ədədlərlə işləmək üçün genişləndirmə
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION