Uzantı Metotları (C# Programlama Kılavuzu)
Uzantı yöntemleri, yeni türetilmiş bir tür oluşturmadan, yeniden derlemeden ya da özgün türü değiştirmeden yöntemler "eklemenizi" sağlar. Uzantı yöntemleri statik yöntemlerdir, ancak genişletilmiş türdeki örnek yöntemler gibi olarak adlandırılırlar. C#, F # ve Visual Basic yazılmış istemci kodu için, genişletme yöntemi ve bir tür içinde tanımlanan yöntemleri çağırma arasında görünür bir fark yoktur.
En yaygın genişletme yöntemleri, var olan ve türlerine sorgu işlevselliği ekleyen LINQ standart sorgu işleçleridir System.Collections.IEnumerable System.Collections.Generic.IEnumerable<T> . Standart sorgu işleçlerini kullanmak için, önce bunları bir using System.Linq yönergeyle kapsama taşıyın. Ardından, uygulayan herhangi bir tür,,, vb IEnumerable<T> . gibi örnek yöntemlere sahip olacak şekilde görünür GroupBy OrderBy Average . Ya da gibi bir türün örneğinden sonra "nokta" yazdığınızda, bu ek yöntemleri IntelliSense deyimin tamamlanmasına bakabilirsiniz IEnumerable<T> List<T> Array .
OrderBy örneği
Aşağıdaki örnek, bir tamsayı dizisinde standart sorgu işleci yönteminin nasıl çağrılacağını gösterir OrderBy . Parantez içindeki ifade bir lambda ifadesidir. Birçok standart sorgu işleci Lambda ifadelerini parametre olarak alır, ancak bu uzantı yöntemleri için bir gereklilik değildir. Daha fazla bilgi için bkz. lambda ifadeleri.
class ExtensionMethods2
{
static void Main()
{
int[] ints = { 10, 45, 15, 39, 21, 26 };
var result = ints.OrderBy(g => g);
foreach (var i in result)
{
System.Console.Write(i + " ");
}
}
}
//Output: 10 15 21 26 39 45
Uzantı yöntemleri statik yöntemler olarak adlandırılır ancak örnek yöntemi söz dizimi kullanılarak çağrılır. İlk parametresi, yöntemin hangi tür üzerinde çalıştığını belirtir. Parametresi öncesinde Bu değiştirici gelir. Uzantı yöntemleri yalnızca bir yönergeyle ad alanını kaynak kodunuza açıkça aktardığınızda kapsam içinde bulunur using .
Aşağıdaki örnek, sınıfı için tanımlanmış bir uzantı yöntemini gösterir System.String . İç içe olmayan, genel olmayan bir statik sınıf içinde tanımlanmıştır:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
WordCountGenişletme yöntemi bu using yönergeyle kapsama getirilebilir:
using ExtensionMethods;
Ve bu sözdizimi kullanılarak bir uygulamadan çağrılabilir:
string s = "Hello Extension Methods";
int i = s.WordCount();
Kodunuzda, örnek yöntemi sözdizimi ile genişletme yöntemi çağırılır. Derleyici tarafından oluşturulan ara dil (IL), kodunuzu statik yöntemdeki bir çağrıya dönüştürür. Kapsülleme ilkesi gerçekten ihlal edilmez. Genişletme yöntemleri genişledikleri türdeki özel değişkenlere erişemez.
Hem MyExtensions sınıfı hem de WordCount yöntemi olur static ve diğer tüm Üyeler gibi erişilebilir static . WordCountYöntemi, static aşağıdaki gibi diğer yöntemler gibi çağrılabilir:
string s = "Hello Extension Methods";
int i = MyExtensions.WordCount(s);
Önceki C# kodu:
- Değeri ile yeni bir adlandırılmış adı bildirir ve atar
strings"Hello Extension Methods". MyExtensions.WordCountBelirtilen bağımsız değişkeni çağırırs
Daha fazla bilgi için bkz. özel bir genişletme yöntemi uygulama ve çağırma.
Genel olarak, büyük olasılıkla kendi uygulamanızı uygulamaktan çok daha sık genişletme yöntemleri çağrılıyor. Genişletme yöntemleri örnek yöntem sözdizimi tarafından çağrıldığından istemci kodundan kullanmak için herhangi bir özel bilgi gerekli değildir. Belirli bir tür için uzantı yöntemlerini etkinleştirmek için, using yöntemlerin tanımlandığı ad alanı için bir yönerge eklemeniz yeterlidir. Örneğin, standart sorgu işleçlerini kullanmak için bu using yönergeyi kodunuza ekleyin:
using System.Linq;
(System.Core.dll için de bir başvuru eklemeniz gerekebilir.) Standart sorgu işleçlerinin artık, çoğu tür için kullanılabilen ek yöntemler olarak IntelliSense 'de göründüğünü fark edeceksiniz IEnumerable<T> .
Derleme Zamanında Uzantı Yöntemleri Bağlama
Bir sınıfı veya arabirimi genişletmek için genişletme yöntemini kullanabilir, ancak bunları geçersiz kılamazsınız. Arabirim veya sınıf yöntemiyle aynı ada ve imzaya sahip genişletme yöntemi asla çağrılmaz. Derleme sırasında genişletme yöntemleri, her zaman türün kendisinde tanımlı örnek yöntemlerden daha düşük önceliğe sahiptir. Diğer bir deyişle, bir türün adlı bir yöntemi varsa Process(int i) ve aynı imzaya sahip bir uzantı yönteminiz varsa, derleyici her zaman örnek yöntemine bağlanır. Derleyici bir yöntem çağırmayla karşılaştığında, türün örnek yöntemleri önce bir eşleşme arar. Eşleşme bulunmazsa, tür için tanımlanan uzantı yöntemleri aranır ve ilk bulunan uzantı yöntemine bağlanılır. Aşağıdaki örnek, derleyicinin hangi genişletme yöntemine veya örnek yöntemine bağlanılacağını nasıl belirlediğini gösterir.
Örnek
Aşağıdaki örnek, C# derleyicisinin bir yöntem çağrısını türde bir örnek yöntemine mi yoksa bir genişletme yöntemine mi bağlayacağını belirlemede izlediği kuralları gösterir. Statik sınıf, Extensions uygulayan her tür için tanımlanmış genişletme yöntemleri içerir IMyInterface . Sınıfları A , B ve C All arabirimini uygular.
MethodBAd ve imza, sınıflar tarafından zaten uygulanmış yöntemlerle tam olarak eşleştiğinden, genişletme yöntemi hiçbir şekilde çağrılmaz.
Derleyici eşleşen imzaya sahip bir örnek yöntemi bulamadığında, varsa eşleşen bir uzantı yöntemine bağlanır.
// Define an interface named IMyInterface.
namespace DefineIMyInterface
{
using System;
public interface IMyInterface
{
// Any class that implements IMyInterface must define a method
// that matches the following signature.
void MethodB();
}
}
// Define extension methods for IMyInterface.
namespace Extensions
{
using System;
using DefineIMyInterface;
// 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)");
}
}
}
// Define three classes that implement IMyInterface, and then use them to test
// the extension methods.
namespace ExtensionMethodsDemo1
{
using System;
using Extensions;
using DefineIMyInterface;
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)");
}
}
class ExtMethodDemo
{
static void Main(string[] args)
{
// 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()
*/
Ortak kullanım desenleri
Koleksiyon Işlevselliği
Geçmişte, System.Collections.Generic.IEnumerable<T> belirli bir tür için arabirimi uygulayan ve bu türdeki koleksiyonlar üzerinde işlem yapan işlevselliği içeren "koleksiyon sınıfları" oluşturmak yaygın bir şekilde oluşturulmuştur. Bu tür koleksiyon nesnesini oluştururken bir sorun olmadığından, üzerinde bir uzantı kullanılarak aynı işlevsellik elde edilebilir System.Collections.Generic.IEnumerable<T> . Uzantılar, işlevin System.Array System.Collections.Generic.List<T> Bu tür üzerinde uygulayan bir veya gibi herhangi bir koleksiyondan çağrılmasına izin vermenin avantajlarından yararlanır System.Collections.Generic.IEnumerable<T> . Bu makalede daha önce, bir Int32 dizisi kullanılarak buna bir örnek bulabilirsiniz.
Layer-Specific Işlevselliği
Çoklu kare mimarisi veya diğer katmanlı uygulama tasarımı kullanırken, bir dizi etki alanı varlığı veya uygulama sınırları genelinde iletişim kurmak için kullanılabilecek nesneleri Veri Aktarımı. Bu nesneler genellikle hiçbir işlevsellik içermez veya yalnızca uygulamanın tüm katmanları için geçerli olan minimum işlevleri içerir. Uzantı yöntemleri, nesneleri gerekli olmayan yöntemlerle yüklemeden veya diğer katmanlarda istemeden, her uygulama katmanına özgü işlevselliği eklemek için kullanılabilir.
public class DomainEntity
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
static class DomainEntityExtensions
{
static string FullName(this DomainEntity value)
=> $"{value.FirstName} {value.LastName}";
}
Önceden tanımlanmış türleri genişletme
Yeniden kullanılabilir işlevlerin oluşturulması gerektiğinde yeni nesneler oluşturmak yerine, genellikle .NET veya CLR türü gibi var olan bir türü genişletebiliriz. örnek olarak, genişletme yöntemlerini kullanmıyorsanız, Engine Query kodumuza ait birden çok konumdan çağrılabilen bir SQL Server sorgu yürütme işini yapmak için bir veya sınıfı oluşturarız. Ancak, System.Data.SqlClient.SqlConnection bir SQL Server bağlantısı olan her yerden bu sorguyu gerçekleştirmek için uzantı yöntemlerini kullanarak sınıfı genişletebilirsiniz. Diğer örnekler, sınıfa ortak işlevsellik eklemek System.String , ve nesnelerinin veri işleme yeteneklerini ve System.IO.File System.IO.Stream System.Exception belirli hata işleme işlevselliği için nesneleri genişletmek olabilir. Bu tür kullanım örnekleri yalnızca hayal ve iyi bir fikir ile sınırlıdır.
Önceden tanımlanmış türleri genişletme, değerler ile struct metotlara geçirdikleri için türlerle zor olabilir. Diğer bir deyişle, yapı üzerinde yapılan tüm değişiklikler yapının bir kopyasına yapılır. Uzantı yöntemi çıktıktan sonra bu değişiklikler görünür değildir. C# 7,2 ile başlayarak, ref bir genişletme yönteminin ilk bağımsız değişkenine değiştirici ekleyebilirsiniz. Değiştirici eklemek, ref ilk bağımsız değişkenin başvuruya göre geçirildiği anlamına gelir. Bu, genişletilmekte olan yapının durumunu değiştiren uzantı yöntemleri yazmanızı sağlar.
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 ancak, uzantı yöntemleri .NET ekosistemi boyunca yeniden kullanılabilir işlevsellik oluşturmak için önemli bir seçenek haline gelir. Özgün kaynak denetiminiz altında olmadığında, türetilmiş bir nesne uygunsuz veya imkansız olduğunda ya da işlevselliğin uygun kapsamın ötesinde sunulmaması durumunda, uzantı yöntemleri mükemmel bir seçimdir.
Türetilmiş türler hakkında daha fazla bilgi için bkz. Devralma.
Kaynak kodunu denetiminde olmayan bir türü genişletmek için bir genişletme yöntemi kullanırken, türün uygulamasındaki bir değişikliğin genişletme yönteminizin kesilmesine neden olacağı riski çalıştırırsınız.
Belirli bir tür için uzantı yöntemleri uygularsanız, aşağıdaki noktaları hatırlayın:
- Türden tanımlı yöntemle aynı imzaya sahip değilse genişletme yöntemi asla çağrılmaz.
- Uzantı yöntemleri ad alanı seviyesinde kapsama alınır. Örneğin, adlı tek bir ad alanında uzantı yöntemlerini içeren birden fazla statik sınıfınız varsa
Extensions, bunların hepsi yönergeyle kapsam haline getirilirusing Extensions;.
Uygulanan bir sınıf kitaplığı için derleme sürüm numarasının artıyor olmasını önlemek için uzantı yöntemleri kullanmamanız gerekir. Kaynak koda sahip olduğunuz bir kitaplığa önemli işlevsellik eklemek istiyorsanız, derleme sürümü oluşturma için .NET yönergelerini izleyin. Daha fazla bilgi için bkz. derleme sürümü oluşturma.
Ayrıca bkz.
- C# Programlama Kılavuzu
- Paralel programlama örnekleri (bunlar birçok örnek genişletme yöntemi içerir)
- Lambda Ifadeleri
- Standart Sorgu İşleçlerine Genel Bakış
- Örnek parametreleri ve bunların etkileri için dönüştürme kuralları
- Diller arasında uzantı yöntemleri birlikte çalışabilirliği
- Uzantı yöntemleri ve curried temsilciler
- Uzantı yöntemi bağlama ve hata raporlama