Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Uzantı üyeleri, yeni türetilmiş bir tür oluşturmadan, yeniden derlemeden veya özgün türü başka bir şekilde değiştirmeden mevcut türlere yöntemleri "eklemenize" olanak tanır.
C# 14'le başlayarak, uzantı yöntemlerini tanımlamak için kullandığınız iki söz dizimi vardır. C# 14, bir tür veya bir tür örneği için birden çok uzantı üyesi tanımladığınız kapsayıcılar ekler extension
. C# 14'ün öncesinde, yöntemin this
parametre türünün bir örneğinin üyesi olarak göründüğünü belirtmek için değiştiriciyi statik yöntemin ilk parametresine eklersiniz.
Uzantı yöntemleri statik yöntemlerdir, ancak genişletilmiş türdeki örnek yöntemleriymiş gibi çağrılır. C#, F# ve Visual Basic ile yazılmış istemci kodu için, uzantı yöntemini çağırma ile bir tür içinde tanımlanan yöntemler arasında belirgin bir fark yoktur. Her iki uzantı yöntemi biçimi de aynı IL(Ara Dil) için derlenir. Uzantı üyelerinin tüketicilerinin uzantı yöntemlerini tanımlamak için hangi söz diziminin kullanıldığını bilmesi gerekmez.
En yaygın uzantı üyeleri, var olan System.Collections.IEnumerable ve System.Collections.Generic.IEnumerable<T> türlerine sorgu işlevselliği ekleyen LINQ standart sorgu işleçleridir. Standart sorgu işleçlerini kullanmak için önce bir using System.Linq
yönergesi ile bunları kapsama alın. Ardından IEnumerable<T> uygulayan herhangi bir türün GroupBy, OrderBy, Averagevb. örnek yöntemlerine sahip olduğu görülür. IntelliSense ifade tamamlama işlemi sırasında, IEnumerable<T> veya List<T> gibi bir Array türünün örneğinden sonra "nokta" yazdığınızda bu ek yöntemleri görebilirsiniz.
OrderBy örneği
Aşağıdaki örnek, bir tamsayı dizisinde standart sorgu işleci OrderBy
yönteminin nasıl çağrılacağını göstermektedir. Parantez içindeki ifade bir lambda ifadesidir. Birçok standart sorgu işleci lambda ifadelerini parametre olarak alır. Daha fazla bilgi için bkz. Lambda İfadeleri.
int[] numbers = [10, 45, 15, 39, 21, 26];
IOrderedEnumerable<int> result = numbers.OrderBy(g => g);
foreach (int i in result)
{
Console.Write(i + " ");
}
//Output: 10 15 21 26 39 45
Uzantı yöntemleri statik yöntemler olarak tanımlanır, ancak örnek yöntemi söz dizimi kullanılarak çağrılır. İlk parametreleri yöntemin hangi tür üzerinde çalıştığını belirtir. Parametre bu değiştiriciyi izler. Uzantı yöntemleri yalnızca ad alanını bir using
yönergesi ile kaynak kodunuz içine açıkça içeri aktardığınızda kapsam dahilindedir.
Uzantı üyelerini bildirme
C# 14'le başlayarak uzantı bloklarını bildirebilirsiniz. İç içe olmayan, jenerik olmayan, statik bir sınıfta, bir türün veya bu türün bir örneğinin uzantı üyelerini içeren bir blok olan uzantı bloğu. Aşağıdaki kod örneği, türü için string
bir uzantı bloğu tanımlar. Uzantı bloğu bir üye içerir: dizedeki sözcükleri sayan bir yöntem:
namespace CustomExtensionMembers;
public static class MyExtensions
{
extension(string str)
{
public int WordCount() =>
str.Split([' ', '.', '?'], StringSplitOptions.RemoveEmptyEntries).Length;
}
}
C# 14'e geçmeden önce, değiştiriciyi this
ilk parametreye ekleyerek bir uzantı yöntemi bildirirsiniz:
namespace CustomExtensionMethods;
public static class MyExtensions
{
public static int WordCount(this string str) =>
str.Split([' ', '.', '?'], StringSplitOptions.RemoveEmptyEntries).Length;
}
Her iki uzantı biçimi de iç içe olmayan,generik olmayan bir statik sınıf içinde tanımlanmalıdır.
Bir uygulamadan, örnek üye erişim söz dizimi kullanılarak çağrılabilir.
string s = "Hello Extension Methods";
int i = s.WordCount();
Uzantı üyeleri mevcut bir türe yeni özellikler eklerken, uzantı üyeleri kapsülleme ilkesini ihlal etmemektedir. Genişletilmiş türün tüm üyeleri için erişim bildirimleri uzantı üyeleri için geçerlidir.
Hem MyExtensions
sınıfı hem de WordCount
yöntemi static
'dir ve diğer tüm static
üyeleri gibi erişilebilir.
WordCount
yöntemi, aşağıdaki gibi diğer static
yöntemleri gibi çağrılabilir:
string s = "Hello Extension Methods";
int i = MyExtensions.WordCount(s);
Yukarıdaki C# kodu, uzantı üyeleri için hem uzantı bloğu this
hem de söz dizimi için geçerlidir. Önceki kod:
- Yeni bir
string
'ı,s
adıyla ve"Hello Extension Methods"
değerine sahip olarak bildirir ve atar. - Çağrı
MyExtensions.WordCount
, verilen bağımsız değişkens
'i çağırır.
Daha fazla bilgi için bkz. Özel uzantı yöntemini uygulama ve çağırma.
Genel olarak, uzantı üyelerini uygulamaktan çok daha sık çağırırsınız. Uzantı üyeleri genişletilmiş sınıfın üyesi olarak bildiriliyormuş gibi çağrıldığından, bunları istemci kodundan kullanmak için özel bir bilgi gerekmez. Belirli bir tür için uzantı üyelerini etkinleştirmek için, yöntemlerin tanımlandığı ad alanı için bir using
yönerge eklemeniz gerekir. Örneğin, standart sorgu işleçlerini kullanmak için bu using
yönergesini kodunuza ekleyin:
using System.Linq;
Derleme zamanında uzantı üyelerini bağlama
Uzantı üyelerini bir sınıfı veya arabirimi genişletmek için kullanabilirsiniz, ancak bir sınıfta tanımlanan davranışı geçersiz kılmak için kullanamazsınız. Arabirim veya sınıf üyeleriyle aynı ada ve imzaya sahip bir uzantı üyesi hiçbir zaman çağrılmaz. Derleme zamanında uzantı üyeleri her zaman türün kendisinde tanımlanan örnek (veya statik) üyelerden daha düşük önceliğe sahiptir. Başka bir deyişle, bir türün adlı Process(int i)
bir yöntemi varsa ve aynı imzaya sahip bir uzantı yönteminiz varsa, derleyici her zaman üye yöntemine bağlanır. Derleyici bir üye çağrısıyla karşılaştığında, önce türün üyelerinde bir eşleşme arar. Eşleşme bulunamadığında, tür için tanımlı herhangi bir uzantı üyesini arar. Bulduğu ilk uzantı üyesine bağlanır. Aşağıdaki örnek, C# derleyicisinin türdeki bir örnek üyesine mi yoksa uzantı üyesine mi bağlanacağını belirlerken izlediği kuralları gösterir. Statik sınıf Extensions
, IMyInterface
uygulayan herhangi bir tür için tanımlanan uzantı üyelerini içerir.
public interface IMyInterface
{
void MethodB();
}
// Define extension methods for IMyInterface.
// The following extension methods can be accessed by instances of any
// class that implements IMyInterface.
public static class Extension
{
public static void MethodA(this IMyInterface myInterface, int i) =>
Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, int i)");
public static void MethodA(this IMyInterface myInterface, string s) =>
Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, string s)");
// This method is never called in ExtensionMethodsDemo1, because each
// of the three classes A, B, and C implements a method named MethodB
// that has a matching signature.
public static void MethodB(this IMyInterface myInterface) =>
Console.WriteLine("Extension.MethodB(this IMyInterface myInterface)");
}
Eşdeğer uzantılar, C# 14 uzantı üyesi söz dizimi kullanılarak bildirilebilir:
public static class Extension
{
extension(IMyInterface myInterface)
{
public void MethodA(int i) =>
Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, int i)");
public void MethodA(string s) =>
Console.WriteLine("Extension.MethodA(this IMyInterface myInterface, string s)");
// This method is never called in ExtensionMethodsDemo1, because each
// of the three classes A, B, and C implements a method named MethodB
// that has a matching signature.
public void MethodB() =>
Console.WriteLine("Extension.MethodB(this IMyInterface myInterface)");
}
}
A
, B
ve C
sınıflarının tümü arabirimi uygular:
// Define three classes that implement IMyInterface, and then use them to test
// the extension methods.
class A : IMyInterface
{
public void MethodB() { Console.WriteLine("A.MethodB()"); }
}
class B : IMyInterface
{
public void MethodB() { Console.WriteLine("B.MethodB()"); }
public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
}
class C : IMyInterface
{
public void MethodB() { Console.WriteLine("C.MethodB()"); }
public void MethodA(object obj)
{
Console.WriteLine("C.MethodA(object obj)");
}
}
adı ve imzası sınıflar tarafından zaten uygulanan yöntemlerle tam olarak eşleştiğinden MethodB
uzantısı yöntemi hiçbir zaman çağrılmaz. Derleyici eşleşen imzaya sahip bir örnek yöntemi bulamadıysa, varsa eşleşen bir uzantı yöntemine bağlanır.
// Declare an instance of class A, class B, and class C.
A a = new A();
B b = new B();
C c = new C();
// For a, b, and c, call the following methods:
// -- MethodA with an int argument
// -- MethodA with a string argument
// -- MethodB with no argument.
// A contains no MethodA, so each call to MethodA resolves to
// the extension method that has a matching signature.
a.MethodA(1); // Extension.MethodA(IMyInterface, int)
a.MethodA("hello"); // Extension.MethodA(IMyInterface, string)
// A has a method that matches the signature of the following call
// to MethodB.
a.MethodB(); // A.MethodB()
// B has methods that match the signatures of the following
// method calls.
b.MethodA(1); // B.MethodA(int)
b.MethodB(); // B.MethodB()
// B has no matching method for the following call, but
// class Extension does.
b.MethodA("hello"); // Extension.MethodA(IMyInterface, string)
// C contains an instance method that matches each of the following
// method calls.
c.MethodA(1); // C.MethodA(object)
c.MethodA("hello"); // C.MethodA(object)
c.MethodB(); // C.MethodB()
/* Output:
Extension.MethodA(this IMyInterface myInterface, int i)
Extension.MethodA(this IMyInterface myInterface, string s)
A.MethodB()
B.MethodA(int i)
B.MethodB()
Extension.MethodA(this IMyInterface myInterface, string s)
C.MethodA(object obj)
C.MethodA(object obj)
C.MethodB()
*/
Yaygın kullanım kalıpları
Koleksiyon İşlevselliği
Geçmişte, belirli bir tür için System.Collections.Generic.IEnumerable<T> arabirimini uygulayan ve bu türdeki koleksiyonlar üzerinde hareket eden işlevler içeren "Koleksiyon Sınıfları" oluşturmak yaygındı. Bu tür bir koleksiyon nesnesi oluşturmada bir sorun olmasa da, System.Collections.Generic.IEnumerable<T>üzerinde bir uzantı kullanılarak aynı işlev elde edilebilir. Uzantılar, işlevselliğin bu türdeki System.Array uygulayan bir System.Collections.Generic.List<T> veya System.Collections.Generic.IEnumerable<T> gibi herhangi bir koleksiyondan çağrılmasına izin verme avantajına sahiptir. Int32 Dizisi kullanılarak bunun bir örneği, bu makalenin önceki bölümlerinde bulunabilir.
Layer-Specific İşlevsellik
Bir Soğan Mimarisi veya diğer katmanlı uygulama tasarımı kullanılırken, uygulama sınırları arasında iletişim kurmak için kullanılabilecek bir etki alanı varlıkları veya Veri Aktarım Nesneleri kümesine sahip olmak yaygın bir durumdur. Bu nesneler genellikle hiçbir işlev veya uygulamanın tüm katmanları için geçerli olan en düşük işlevsellik içerir. Uzantı yöntemleri, her uygulama katmanına özgü işlevler eklemek için kullanılabilir.
public class DomainEntity
{
public int Id { get; set; }
public required string FirstName { get; set; }
public required string LastName { get; set; }
}
static class DomainEntityExtensions
{
static string FullName(this DomainEntity value)
=> $"{value.FirstName} {value.LastName}";
}
Yeni uzantı bloğu söz dizimini kullanarak C# 14 ve sonraki sürümlerde eşdeğer FullName
bir özellik bildirebilirsiniz:
static class DomainEntityExtensions
{
extension(DomainEntity value)
{
string FullName => $"{value.FirstName} {value.LastName}";
}
}
Önceden Tanımlanmış Türleri Genişletme
Yeniden kullanılabilir işlevselliğin oluşturulması gerektiğinde yeni nesneler oluşturmak yerine, genellikle .NET veya CLR türü gibi mevcut bir türü genişletebilirsiniz. Örneğin, uzantı yöntemlerini kullanmıyorsanız, kodumuzda birden çok yerden çağrılabilecek bir SQL Server'da sorgu yürütme işini yapmak için bir veya Engine
sınıfı oluşturabilirsinizQuery
. Bununla birlikte, sql server ile bağlantınız olan her yerden bu sorguyu gerçekleştirmek için uzantı yöntemlerini kullanarak sınıfını genişletebilirsiniz System.Data.SqlClient.SqlConnection . Diğer örnekler, System.String sınıfına ortak işlevler eklemek, System.IO.Stream nesnesinin veri işleme özelliklerini genişletmek ve belirli hata işleme işlevselliği için nesneleri System.Exception olabilir. Bu tür kullanım örnekleri yalnızca hayal gücünüz ve sağduyunuzla sınırlıdır.
Önceden tanımlanmış türlerin, struct
türleriyle genişletilmesi, bunlar yöntemlere değer olarak geçirildiği için zor olabilir. Bu, yapıda yapılan tüm değişikliklerin yapının bir kopyasında yapıldığı anlamına gelir. Uzantı yöntemi çıktıktan sonra bu değişiklikler görünmez. İlk bağımsız değişkene ref
değiştiricisini ekleyerek onu ref
genişletme metodu haline getirebilirsiniz.
ref
anahtar sözcüğü, herhangi bir anlam farkı olmadan this
anahtar sözcüğünden önce veya sonra görünebilir.
ref
değiştiricisinin eklenmesi, ilk parametrenin başvuruyla geçirildiğini gösterir. Bu teknik, genişletilmiş yapının durumunu değiştiren uzantı yöntemleri yazmanızı sağlar (özel üyelerin erişilebilir olmadığını unutmayın). Yalnızca yapıyla kısıtlanmış değer türlerine veya genel türlere (Bu kurallar hakkında daha fazla bilgi için bkz. kısıtlamaya bakınstruct
) uzantı ref
yönteminin ilk parametresi veya uzantı bloğunun alıcısı olarak izin verilir. Aşağıdaki örnekte, sonucu yeniden atamaya gerek kalmadan yerleşik bir türü doğrudan değiştirmek veya ref
anahtar sözcüğüyle bir işlevden geçirmek için ref
uzantısı yönteminin nasıl kullanılacağı gösterilmektedir:
public static class IntExtensions
{
public static void Increment(this int number)
=> number++;
// Take note of the extra ref keyword here
public static void RefIncrement(this ref int number)
=> number++;
}
Eşdeğer uzantı blokları aşağıdaki kodda gösterilir:
public static class IntExtensions
{
extension(int number)
{
public void Increment()
=> number++;
}
// Take note of the extra ref keyword here
extension(ref int number)
{
public void RefIncrement()
=> number++;
}
}
Alıcı için değere ve başvuruya göre parametre modlarını ayırt etmek için farklı uzantı blokları gerekir.
Alıcıya uygulanan ref
farkı aşağıdaki örnekte görebilirsiniz:
int x = 1;
// Takes x by value leading to the extension method
// Increment modifying its own copy, leaving x unchanged
x.Increment();
Console.WriteLine($"x is now {x}"); // x is now 1
// Takes x by reference leading to the extension method
// RefIncrement changing the value of x directly
x.RefIncrement();
Console.WriteLine($"x is now {x}"); // x is now 2
Kullanıcı tanımlı yapı türlerine uzantı üyeleri ekleyerek ref
aynı tekniği uygulayabilirsiniz:
public struct Account
{
public uint id;
public float balance;
private int secret;
}
public static class AccountExtensions
{
// ref keyword can also appear before the this keyword
public static void Deposit(ref this Account account, float amount)
{
account.balance += amount;
// The following line results in an error as an extension
// method is not allowed to access private members
// account.secret = 1; // CS0122
}
}
Yukarıdaki örnek, C# 14'teki uzantı blokları kullanılarak da oluşturulabilir:
public static class AccountExtensions
{
extension(ref Account account)
{
// ref keyword can also appear before the this keyword
public void Deposit(float amount)
{
account.balance += amount;
// The following line results in an error as an extension
// method is not allowed to access private members
// account.secret = 1; // CS0122
}
}
}
Bu uzantı yöntemlerine aşağıdaki gibi erişebilirsiniz:
Account account = new()
{
id = 1,
balance = 100f
};
Console.WriteLine($"I have ${account.balance}"); // I have $100
account.Deposit(50f);
Console.WriteLine($"I have ${account.balance}"); // I have $150
Genel Yönergeler
Bir nesnenin kodunu değiştirerek veya makul ve mümkün olduğunda yeni bir tür türeterek işlevsellik eklemek tercih edilir. Uzantı yöntemleri, .NET ekosistemi genelinde yeniden kullanılabilir işlevsellik oluşturmak için kritik bir seçenektir. Uzantı üyeleri, özgün kaynak denetiminiz altında olmadığında, türetilmiş bir nesne uygun olmadığında veya imkansız olduğunda ya da işlevselliğin sınırlı kapsamı olduğunda tercih edilir.
Türetilmiş türler hakkında daha fazla bilgi için bkz Devralma.
Belirli bir tür için uzantı yöntemleri uygularsanız aşağıdaki noktaları unutmayın:
- Uzantı yöntemi, türünde tanımlanan bir yöntemle aynı imzaya sahipse çağrılmaz.
- Uzantı yöntemleri, ad alanı düzeyinde kapsama getirilir. Örneğin, adlı
Extensions
tek bir ad alanında uzantı yöntemleri içeren birden çok statik sınıfınız varsa, bunların tümü yönergesiusing Extensions;
tarafından kapsama alınır.
Uyguladığınız bir sınıf kitaplığı için, derlemenin sürüm numarasını artırmaktan kaçınmak için uzantı yöntemlerini kullanmamalısınız. Kaynak kodun sahibi olduğunuz bir kitaplığa önemli işlevler eklemek istiyorsanız, derleme sürümü oluşturma için .NET yönergelerini izleyin. Daha fazla bilgi için bkz. Derleme Sürümleme .
Ayrıca bkz.
- Paralel Programlama Örnekleri (birçok örnek uzantı yöntemlerini gösterir)
- Lambda İfadeleri
- Standart Sorgu İşleçlerine Genel Bakış
- Örnek parametreler ve bunların etkisi için Dönüştürme kuralları
- Uzantı yöntemleri Diller arasında birlikte çalışabilirlik
- Uzantı yöntemleri ve Curried Delegates
- Uzantı yöntemi Bağlama ve Hata raporlama