Uzatma Yöntemlerinin Hangi Avantajlarını buldunuz? [kapalı]


84

Bir C # "inanmayan", genişletme yöntemlerinin amacının ne olduğunu soruyordu. Daha sonra, özellikle orijinal nesnenin kaynağına sahip olmadığınızda / kontrol etmediğinizde, önceden tanımlanmış nesnelere yeni yöntemler ekleyebileceğinizi açıkladım.

"Neden kendi sınıfınıza bir yöntem eklemiyorsunuz?" Dönüp duruyoruz (iyi bir şekilde). Genel cevabım, alet kayışındaki başka bir araç olduğu ve cevabı, bir aletin yararsız bir israfı olduğu ... ama daha "aydınlanmış" bir cevap alacağımı düşündüm.

Kendi sınıfınıza eklenen bir yöntemi kullanamadığınız (veya kullanmamanız gereken) uzantı yöntemlerini kullandığınız bazı senaryolar nelerdir?


2
Uzatma yöntemlerini desteklemek için insanların yapabileceği (ve yapmış olduğu) kesinlikle geçerli noktalar olduğunu düşünüyorum ... ama kesinlikle uzatma yöntemleri yerine statik yöntemler kullanmanın " mümkün olmadığı " bir senaryo yok . Uzatma yöntemleri gerçekten vardır farklı bir şekilde erişilebilir sadece statik yöntemler. Sadece akılda tutulması gereken bir şey.
Dan Tao

@DanTao, daha açık bir not olarak, uzantı yöntemiyle aramayı yeri doldurulamaz kılan tek şey onların isimlendirmeleridir. Extensions.To(1, 10)anlamsız ve 1.To(10)açıklayıcıdır. Tabii ki bahsettiğiniz teknik tarafı anlıyorum, sadece söylüyorum. Aslında bir "senaryo vardır olamazdı statik yöntemlerle, örneğin yansıma yerine kullanılan uzatma yaklaşım", başka bir durumdur dynamic.
nawfal

Yanıtlar:


35

Uzantı yöntemlerinin kod yazarken çok yardımcı olduğunu düşünüyorum, temel türlere uzantı yöntemleri eklerseniz, bunları intellisense ile daha hızlı alacaksınız.

Dosya boyutunu biçimlendirmek için biçim sağlayıcım var . Kullanmak için yazmam gerekiyor:

Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize));

Yazabileceğim bir uzatma yöntemi oluşturmak:

Console.WriteLine(fileSize.ToFileSize());

Daha temiz ve daha basit.


4
uzantı yönteminiz için daha açıklayıcı bir ad, onu daha da temiz hale getirir. örneğin fileSize.ToFormattedString ();
David Alpert

1
mmm, ToFormattedFizeSize () belki, bu bir Long türü için bir uzatma yöntemidir, ToFormattedString () açıklayıcı bir ad değildir
Eduardo Campañó

3
yine de noktaya değindi. fileSize.ToFileSize () okuduğumda neden yinelemeyi soruyorum? aslında ne yapıyor? Bunun sadece bir örnek olduğunu biliyorum, ancak açıklayıcı isimler onu temiz ve basit hale getirmeye yardımcı oluyor.
David Alpert

1
haklısın çünkü testlerde doğru ismi seçmek çok önemli
Eduardo Campañó

5
Sağladığınız örneğin bir uzatma yöntemi olması gerekmediğinden, bu gerçekten soruyu doğrudan yanıtlamıyor. Geçen ay, LongToFileSize(fileSize)sadece, Özlü gibidir ve belki tıpkı temizleyin, hangi.
Dan Tao

83

Sadece uzatma yöntemlerinin avantaj kod okunabilirliği olduğunu. Bu kadar.

Uzatma yöntemleri bunu yapmanıza izin verir:

foo.bar();

bunun yerine:

Util.bar(foo);

Şimdi C # 'da bunun gibi birçok şey var. Başka bir deyişle, C # 'da önemsiz görünen ve kendi başına büyük bir faydası olmayan birçok özellik vardır. Ancak bu özellikleri bir araya getirmeye başladığınızda, parçalarının toplamından biraz daha büyük bir şey görmeye başlarsınız. LINQ, uzantı yöntemlerinden büyük ölçüde yararlanır, çünkü LINQ sorguları bunlar olmadan neredeyse okunamaz olur. LINQ, genişletme yöntemleri olmadan mümkün olabilir , ancak pratik değildir.

Uzatma yöntemleri, C # 'ın kısmi sınıflarına çok benzer. Kendi başlarına pek yardımcı olmuyorlar ve önemsiz görünüyorlar. Ancak, kod üretilmesi gereken bir sınıfla çalışmaya başladığınızda, kısmi sınıflar çok daha anlamlı olmaya başlar.


1
X.Foo (bir şey) .Bar (başka bir şey) .Baz (henüz bir şey) gibi zincirleme yöntemler kullanıyorsanız, bu daha da önemli hale gelir. Bu, IEnumerable <> uzatma yöntemlerinde en yaygın olanıdır ve okunabilirliği önemli ölçüde artırır
ShuggyCoUk

2
Anlamadığım şey, foo.bar () 'ın bana bar ()' ın bir foo yöntemi olduğunu önermesi. Bu yüzden işe yaramadığında yanlış yere bakıyorum. Bunun benim için bir okunabilirlik artışı olduğundan emin değilim.
Martin Brown

3
VS IDE'de çubuğa () sağ tıklayıp Tanıma Git'i seçmek sizi doğru yere getirecektir.
Jeff Martin

9
Dil yapılarının% 98'inin tek avantajı kod okunabilirliğidir. Şube operatörü, etiket, gitme ve artırma dışında her şey hayatınızı biraz kolaylaştırmak için var.
Giovanni Galbo

2
Bu tamamen doğru değil, bazı durumlar vardır ki, bir sınıfı genişletmenin tek yolu genişletme metotlarıdır . Mühürlü derslerin yanı sıra, onları oldukça benzersiz bir durumda kullanıyorum. Aşağıdaki yazıma bakın.
Edwin Jarvis

34

Alet yapmayı unutma! Foo türüne bir uzantı yöntemi M eklediğinizde, Foo'nun intellisense listesinde 'M' elde edersiniz (uzantı sınıfının kapsam dahilinde olduğu varsayılarak). Bu, 'M'yi bulmayı Sınıfım M'den (Foo, ...) çok daha kolay hale getirir.

Günün sonunda, bu sadece başka yerlerdeki statik yöntemler için sözdizimsel şekerdir, ancak bir ev satın almak gibi: 'konum, konum, konum!' Türüne bağlı kalırsa, insanlar onu bulacaktır!


2
Bununla ilgili hafif bir olumsuzluk da var: genişletme yöntemleri (System.Linq gibi, IntelliSense otomatik tamamlamayı daha uzun bir listeyle doldurarak gezinmeyi biraz daha zorlaştırıyor.
Jared Updike,

@Jared: ... ancak yalnızca uzantı yöntemlerinin bulunduğu ad alanını içe aktardıysanız, aslında bu "IntelliSense kirliliği" üzerinde küçük bir kontrol derecesine sahip olursunuz. İkinci olarak, BCL'de Stringoldukça uzun bir örnek yöntem listesi ile gelen sınıflar olduğunu unutmayalım , bu nedenle bu sorun genişletme yöntemlerine özgü değildir.
stakx - artık

29

Karşılaştığım uzatma yöntemlerinin iki faydası daha:

  • Akıcı bir arayüz, statik bir uzatma yöntemleri sınıfında kapsüllenebilir, böylece çekirdek sınıf ve onun akıcı uzantıları arasındaki endişelerin ayrılması sağlanır; Daha fazla bakım kolaylığı sağladığını gördüm.
  • Uzantı yöntemleri arabirimlere asılabilir, böylece bir sözleşme (bir arabirim aracılığıyla) ve ilişkili bir dizi arabirim tabanlı davranış (uzatma yöntemleri aracılığıyla) belirtmenize izin vererek yine endişelerin ayrılmasını sağlar. Bir örnek yöntemleri gibi Linq uzantısı olan Select(...), Where(...)kapalı, vb Hung IEnumerable<T>arabirimi.

1
Daha iyi anlamalarına yardımcı olmak için her iki nokta için de bazı kod örnekleri aktarabilir misiniz? İlginç bir bakış açısı gibi görünüyor.
RBT

evet 2. nokta için bir örnek güzel olurdu.
Ini

26

Uzatma yöntemleri için sahip olduğum en iyi kullanımlardan bazıları şu beceridir:

  1. Çoğu durumda olarak işaretlenecek olan üçüncü şahıs nesnelerindeki (ticari veya şirket içi, ancak ayrı bir grup tarafından yönetilen) işlevselliği genişletin sealed.
  2. Soyut bir sınıf uygulamak zorunda kalmadan arayüzler için varsayılan işlevsellik oluşturun

Örneğin IEnumerable<T>,. Uzantı yöntemleri açısından zengin olsa da, genel bir ForEach yöntemi uygulamamasını can sıkıcı buldum. Ben de kendim yaptım:

public void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
    foreach ( var o in enumerable )
    {
        action(o);
    }
}

Voila, IEnumerable<T>uygulama türünden bağımsız olarak tüm nesnelerim ve yazıp yazmadığım veya başka birinin koduma ForEachuygun bir "using" ifadesi ekleyerek artık bir yöntemi vardı.


3
+1 Bu uzantıyı seviyorum, aynen onun gibi bir tane yazdım. Çok kullanışlı
Maslow

10
Yöntemin statik olması gerektiğini unutmayın. Bu uzantıyı kullanmaya karar vermeden önce blogs.msdn.com/ericlippert/archive/2009/05/18/… okumanızı öneririm .
TrueWill

IEnumerable'da ForEach uzantı yöntemini uygulamanın neden takdir edilmediğinin nedeni
RBT

12

Uzantı yöntemlerini kullanmanın en büyük nedenlerinden biri LINQ. Uzatma yöntemleri olmadan LINQ'da yapabileceklerinizin çoğu çok zor olurdu. Where (), Contains (), Select extension yöntemleri, yapılarını değiştirmeden mevcut türlere çok daha fazla işlevsellik eklendiği anlamına gelir.


4
onun 'biri' değil, uzatma yöntemlerinin nedeni.
gbjbaanb

3
Nasıl yani? Tek gereksinim değil, uzatma yöntemlerine sahip olmanın birçok nedeninden biridir. LINQ kullanmadan uzatma yöntemlerini kullanabilirim.
Ray Booysen 09

Sanırım yaratılmalarının sebebinin bu olduğunu kastetti ama evet, kulağa onlar için tek uygun kullanım gibi geliyor.
Brent Rittenhouse

9

Uzantı yöntemlerinin avantajları hakkında pek çok cevap var ; dezavantajları ele almaya ne dersiniz ?

En büyük dezavantajı, normal bir yönteminiz ve aynı bağlamda aynı imzaya sahip bir uzantı yönteminiz varsa derleyici hatası veya uyarısı olmamasıdır.

Belirli bir sınıfa uygulanan bir genişletme yöntemi oluşturduğunuzu varsayalım. Daha sonra birisi o sınıfın kendisinde aynı imzaya sahip bir yöntem oluşturur.

Kodunuz derlenecek ve bir çalışma zamanı hatası bile almayabilirsiniz. Ancak artık eskisi gibi aynı kodu çalıştırmıyorsunuz.


1
4. Sahip olmadığınız türleri genişletmeden önce iki kez düşünün . Yalnızca sahip olduğunuz türler için uzantı yöntemleri yazarsanız, uzantılarınızın genişletilmiş türdeki değişikliklerle bozulacağı konusunda asla endişelenmenize gerek kalmaz. Öte yandan, diğer insanları türlerini genişletiyorsanız, esasen onların insafına kalıyorsunuz.
DavidRR

1
... Alternatif olarak, başkası tarafından kullanılması muhtemel olmayan isimleri seçerek veya tanımladığınız toplam genişletme yöntemi sayısını en aza indirerek (yani yüzey alanını azaltarak) bu riski en aza indirebilirsiniz. Bunu yapmak için bir teknik, temeldeki nesneyi sizin tarafınızdan kontrol edilen farklı bir türe dönüştüren bir uzantı yöntemi yazmak olabilir.
DavidRR

Uzantı Yöntemleri (C # Programlama Kılavuzu) : Genel olarak, muhtemelen uzantı yöntemlerini kendi yöntemlerinizi uygulamaktan çok daha sık çağıracaksınız.
DavidRR


4

Uzatma yöntemleri için kişisel argümanım, bir OOP tasarımına çok iyi uyduklarıdır: basit yöntemi düşünün

bool empty = String.IsNullOrEmpty (myString)

kıyasla

bool empty = myString.IsNullOrEmpty ();

Örneğinizin OOP ile ne ilgisi olduğunu anlamıyorum. Ne demek istiyorsun?
Andrew Hare

1
yöntemin nesneye ait olduğu "görünüyor"
Bluenuance

3
Bu kötü bir örnek (NullReferenceException atabilir) ama doğru yolda. Daha iyi bir örnek Math.abs (-4) yerine -4.abs () gibi bir şey koymak olabilir;
Outlaw Programmer

15
Bildiğim kadarıyla (ve tecrübelerime göre, eğer bellek hizmet ediyorsa), myString.IsNullOrEmpty () 'nin bir NullReferenceException oluşturmasına gerek yoktur, çünkü uzatma yöntemleri ateşlemek için bir nesne örneği gerektirmez.
David Alpert

1
Şahsen, boş bir örnekle çalışması amaçlanan genişletme yöntemlerinin hayranı değilim - okuyucuya bir şey gibi görünüyor, ancak sonra başka bir şey yapıyor.
Mark Simpson

4

Yukarıda hangi uzatma yöntemlerinin yapmanıza izin verdiğiyle ilgili çok sayıda harika yanıt var.

Kısa cevabım - fabrika ihtiyacını neredeyse tamamen ortadan kaldırıyorlar.

Yeni bir kavram olmadıklarını ve en büyük doğrulamalarından birinin Objective-C ( kategoriler ) 'de katil bir özellik oldukları olduğunu belirteceğim . Çerçeve tabanlı geliştirmeye o kadar çok esneklik katarlar ki, NeXT'nin büyük kullanıcılar olarak NSA ve Wall Street finansal modelleyicileri vardır.

REALbasic ayrıca bunları genişletilmiş yöntemler olarak uygular ve geliştirmeyi basitleştiren benzer kullanımları vardır.


4

Burada, uzantı yöntemlerinin arkasında önemli bir neden olarak geliştirilmiş kod okunabilirliğinden bahseden diğer yanıtları desteklemek istiyorum. Bunu iki yönüyle göstereceğim: yöntem zincirleme ve yuvalanmış yöntem çağrıları ve anlamsız statik sınıf adlarıyla bir LINQ sorgusunun yığılması.


Bu LINQ sorgusunu örnek olarak alalım:

numbers.Where(x => x > 0).Select(x => -x)

Her ikisi Whereve Selectstatik sınıfta tanımlanan uzantı yöntemleridir Enumerable. Bu nedenle, eğer uzatma yöntemleri yoksa ve bunlar normal statik yöntemler ise, son kod satırı aslında şu şekilde görünmelidir:

Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x)

Bu sorgunun ne kadar kötü olduğunu görün.


İkincisi, şimdi kendi sorgu işlecinizi tanıtmak istiyorsanız, doğal olarak onu Enumerablediğer tüm standart sorgu işleçleri gibi statik sınıf içinde tanımlamanın hiçbir yolu olmazdı , çünkü Enumerableçerçevede ve bu sınıf üzerinde hiçbir kontrolünüz yok. Bu nedenle, uzantı yöntemlerini içeren kendi statik sınıfınızı tanımlamanız gerekir. Daha sonra bunun gibi sorgular alabilirsiniz:

Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x)
//                ^^^^^^^^^^^^^^^^^^^^^^
//                different class name that has zero informational value
//                and, as with 'Enumerable.xxxxxx', only obstructs the
//                query's actual meaning.

3

(Uzantı) yönteminizi doğrudan sınıfınıza ekleyebileceğiniz doğrudur. Ancak tüm sınıflar sizin tarafınızdan yazılmaz. Çekirdek kitaplıktan veya üçüncü taraf kitaplıklardan sınıflar genellikle kapalıdır ve sözdizimsel şekeri genişletme yöntemleri olmadan elde etmek imkansızdır. Ancak, uzatma yöntemlerinin tıpkı (statik) bağımsız yöntemler gibi olduğunu unutmayın. c ++


3

Uzantı yöntemleri, sınıflarınızın ve sınıf bağımlılıklarınızın temiz kalmasına da yardımcı olabilir. Örneğin, Foo'nun kullanıldığı her yerde Foo sınıfı için bir Bar () yöntemine ihtiyacınız olabilir. Ancak, başka bir derlemede ve yalnızca bu derleme için bir .ToXml () yöntemini isteyebilirsiniz. Bu durumda, gerekli System.Xml ve / veya System.Xml.Linq bağımlılıklarını orijinal derlemeye değil, bu derlemeye ekleyebilirsiniz.

Yararları: Tanımlayıcı sınıf derlemenizdeki bağımlılıklar yalnızca çıplak gereksinimlere indirgenir ve diğer tüketen derlemelerin ToXml () yöntemini kullanması engellenir. Daha fazla referans için bu PDC sunumuna bakın .


3

Uzantı yöntemlerinin kodun okunabilirliğini artırdığına katılıyorum, ancak gerçekten statik yardımcı yöntemlerden başka bir şey değil.

Sınıflarınıza davranış eklemek için uzantı yöntemlerini kullanan IMO şunlar olabilir:

Kafa karıştırıcı: Programcılar, yöntemlerin genişletilmiş türün bir parçası olduğuna inanabilir, bu nedenle uzantı ad alanı içe aktarılmadığında yöntemlerin neden gittiğini anlamayabilir.

Bir antipattern: Uzantı yöntemlerini kullanarak çerçevenizdeki türlere davranış eklemeye karar verirsiniz, ardından bunları birim testine dahil edecek bir kişiye gönderirsiniz. Şimdi, sahte yapamayacağı bir dizi yöntem içeren bir çerçeve ile sıkışmış durumda.


Bir geliştiricinin taklit edemeyeceği bir dizi yöntemle sıkışıp kaldığı doğru değil. Bu uzantı yöntemleri, sonunda, genişlettikleri sınıf / arabirim üzerinde bir yöntemi çağırır ve bu yöntem, sanal olduğu için durdurulabilir.
Patrik Hägne

1
Öyleyse, uzatma yönteminde ne yapıldığına bağlı. Uzatma yöntemleri olduklarını bilmeden, bir arayüzde sağlanan uzantı yöntemlerini kullandığınızı varsayalım. O zaman bir arabirime yapılan bir yöntem çağrısını taklit etmek istemezsiniz, ancak aslında statik bir yöntemi çağırdığınızı fark edersiniz. Statik bir yöntem çağrısı, proxy tabanlı sahte API tarafından engellenemez, bu nedenle tek seçeneğiniz, hangi yöntemlerin sahtesini yapacağınızı bulmak için uzantı yöntemi üzerinde düşünme yapmaktır. IMO birim testi ile birleştirildiğinde hala anti-model.
HaraldV

2

Uzantı yöntemleri, Martin Fowler'ın Kitabından (yöntem imzasına kadar) "Yabancı Yöntemi Tanıtma" yeniden düzenleyicisinin .NET birleşimidir . Temelde aynı faydalar ve tuzaklarla gelirler. Bu refactor ile ilgili bölümde, yönteme gerçekten sahip olması gereken sınıfı değiştiremediğiniz zamanlar için bir çözüm olduğunu söylüyor.


2
+1, ancak uzantı yöntemlerini arayüzlerle de kullanabileceğinizi unutmayın.
TrueWill

2

Genişletme yöntemlerini, belki de serbest işlevlere izin vermemeleri gerektiğinin bir kabulü olarak görüyorum.

C ++ topluluğunda, üyeler yerine ücretsiz üye olmayan işlevleri tercih etmek genellikle iyi bir OOP uygulaması olarak kabul edilir, çünkü bu işlevler ihtiyaç duymadıkları özel üyelere erişim sağlayarak kapsüllemeyi bozmaz. Uzatma yöntemleri, aynı şeyi başarmanın dolambaçlı bir yolu gibi görünüyor. Yani, özel üyelere erişimi olmayan statik işlevler için daha temiz bir sözdizimi.

Uzatma yöntemleri, sözdizimsel şekerden başka bir şey değildir, ancak bunları kullanmanın bir zararı görmüyorum.


2
  • Çirkin bir yardımcı program işlevini çağırmak yerine nesnenin kendisinde Intellisense
  • Dönüştürme işlevleri için, "XToY (X x)" değerini "ToY (bu X x)" olarak değiştirebilir, bu da çirkin XToY (x) yerine güzel x.ToY () ile sonuçlanır.
  • Kontrolünüzün olmadığı sınıfları genişletin
  • Sınıfların kendilerine yöntem eklemek istenmediğinde sınıfların işlevselliğini genişletin. Örneğin, iş nesnelerini basit ve mantıksız tutabilir ve uzantı yöntemlerinde çirkin bağımlılıklara sahip belirli iş mantığı ekleyebilirsiniz.

2

Nesne modeli sınıflarımı yeniden kullanmak için onları kullanıyorum. Veritabanında sahip olduğum nesneleri temsil eden bir sürü sınıfım var. Bu sınıflar istemci tarafında yalnızca nesneleri görüntülemek için kullanılır, bu nedenle temel kullanım özelliklere erişmektir.

public class Stock {
   public Code { get; private set; }
   public Name { get; private set; }
}

Bu kullanım modeli nedeniyle bu sınıflarda iş mantığı yöntemlerine sahip olmak istemiyorum, bu yüzden her iş mantığını bir genişletme yöntemi yapıyorum.

public static class StockExtender {
    public static List <Quote> GetQuotesByDate(this Stock s, DateTime date)
    {...}
}

Bu şekilde, aynı sınıfları iş mantığı işleme ve istemci tarafını gereksiz kodla aşırı yüklemeden kullanıcı arabirimi görüntüleme için kullanabilirim.

Bu çözümle ilgili ilginç bir şey, nesne modeli sınıflarımın Mono.Cecil kullanılarak dinamik olarak oluşturulmasıdır , bu nedenle istesem bile iş mantığı yöntemleri eklemek çok zor olurdu. XML tanımlama dosyalarını okuyan ve veritabanında sahip olduğum bazı nesneleri temsil eden bu saplama sınıflarını oluşturan bir derleyicim var. Bu durumda tek yaklaşım onları genişletmektir.


1
Bunun için kısmi sınıfları kullanabilirsiniz ...
JJoos


1

Son projemde, iş nesnelerine Validate () yöntemlerini eklemek için uzantı yöntemini kullandım. Bunu haklı çıkardım çünkü serileştirilebilir veri aktarım nesnelerinin bulunduğu ve ürün, müşteri, tüccar gibi genel e-ticaret varlıklarının olduğu gibi farklı alanlarda kullanılacak iş nesneleri. Farklı alanlarda iş kuralları da farklı olabilir, bu yüzden Veri aktarım nesnelerimin temel sınıfına atanan bir Validate yöntemindeki geç bağlı doğrulama mantığı. Umarım bu mantıklıdır :)


1

Uzantı yöntemlerinin oldukça kullanışlı olduğu bir durum, ASMX web hizmetlerini kullanan bir istemci uygulamasındaydı. Serileştirme nedeniyle, web yöntemlerinin dönüş türleri herhangi bir yöntem içermez (yalnızca bu türlerin genel özellikleri istemcide mevcuttur).

Uzantı yöntemleri, istemci tarafında başka bir nesne modeli veya çok sayıda sarmalayıcı sınıfı oluşturmak zorunda kalmadan web yöntemlerinin döndürdüğü türlere işlevsellik (istemci tarafında) eklemek için kullanıma izin verdi.


1

Genişletme yöntemleri bir tür karışım oluşturmak için kullanılabilir C # 'da .

Bu da, ortogonal kavramlar için endişelerin daha iyi ayrılmasını sağlar. Bu cevaba bir bak .

Bu aynı zamanda DCI mimarisinin merkezi bir konsept olan C # 'da rolleri etkinleştirmek için kullanılabilir .


1

Ayrıca, C # stillerinde kullanıldığında Linq sorgusunun daha okunabilir olmasına yardımcı olmak için uzatma yöntemlerinin eklendiğini unutmayın.

Bu 2 etki kesinlikle eşdeğerdir, ancak ilki çok daha okunabilirdir (ve okunabilirlikteki boşluk elbette daha fazla yöntem zincirlendiğinde artacaktır).

int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last();

int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));

Tam nitelikli sözdiziminin şöyle olması gerektiğini unutmayın:

int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>();

int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));

Şans eseri, türü parametreleri Whereve Lastbu iki yöntemden ilk parametre (anahtar kelimeye göre tanıtıldı parametresi varlığında sayesinde infered edilebilir olarak, açıkça belirtilmelidir gerekmezthis ve onları uzantısı yöntemlerini olun).

Bu nokta, uzatma yöntemlerinin (diğerlerinin yanı sıra) açıkça bir avantajıdır ve yöntem zincirlemesinin dahil olduğu her benzer senaryoda bundan faydalanabilirsiniz.

Özellikle, herhangi bir alt sınıf tarafından çağrılabilen ve bu alt sınıfa (alt sınıf türü ile) güçlü bir şekilde yazılmış bir referans döndüren bir temel sınıf yöntemine sahip olduğumu bulduğum daha zarif ve ikna edici yoldur.

Örnek (tamam, bu senaryo tamamen sevimsiz): iyi bir geceden sonra, bir hayvan gözlerini açar ve sonra ağlar; her hayvan aynı şekilde gözlerini açar, oysa bir köpek havlar ve bir ördek kvağı.

public abstract class Animal
{
    //some code common to all animals
}

public static class AnimalExtension
{
    public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return animal; //returning a self reference to allow method chaining
    }
}

public class Dog : Animal
{
    public void Bark() { /* ... */ }
}

public class Duck : Animal
{
    public void Kwak() { /* ... */ }
}

class Program
{
    static void Main(string[] args)
    {
        Dog Goofy = new Dog();
        Duck Donald = new Duck();
        Goofy.OpenTheEyes().Bark(); //*1
        Donald.OpenTheEyes().Kwak(); //*2
    }
}

Kavramsal OpenTheEyesolarak bir Animalyöntem olmalı , ancak daha sonra soyut sınıfın bir örneğini döndürecektir Animal, bu da belirli alt sınıf yöntemlerini Barkya Duckda her neyse bilmeyen . * 1 ve * 2 olarak yorumlanan 2 satır daha sonra bir derleme hatası doğurur.

Ancak genişletme yöntemleri sayesinde, "çağrıldığı alt sınıf türünü bilen bir temel yönteme" sahip olabiliriz.

Basit bir jenerik yöntemin işi başarmış olabileceğini, ancak çok daha garip bir şekilde:

public abstract class Animal
{
    //some code common to all animals

    public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return (TAnimal)this; //returning a self reference to allow method chaining
    }
}

Bu sefer parametre ve dolayısıyla olası dönüş türü çıkarımı yok. Çağrı şunlardan başka bir şey olamaz:

Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();

... daha fazla zincirleme söz konusuysa kodu çok ağırlaştırabilir (özellikle tür parametresinin her zaman <Dog>Goofy'nin satırında ve <Duck>Donald'ın satırında olacağını bilmek ...)


1
İlk defa "kwak" ı görüyorum. Bu başka bir ülkede yaygın olan bir yazım mı? Yazıldığını gördüğüm tek yol "şarlatan".
Brent Rittenhouse

0

Bununla ilgili anlatacak tek bir kelimem var: SÜRDÜRÜLEBİLİRLİK Bu, uzantı yöntemlerinin kullanımının anahtarıdır


4
Sanırım birden fazla kelimeye ihtiyacınız olabilir çünkü bunun ne anlama geldiğini anlamıyorum.
Outlaw Programmer

2
Aslında bunun genişletme yöntemlerine karşı bir argüman olduğunu söyleyebilirim . Genişletme yöntemlerinin risklerinden biri, herkesin yarattığı, her yerde olabilmeleridir. Dikkatli kullanılmazsa yabani büyüme meydana gelebilir.
Boris Callens

EM kullanırken tüm referansları kolayca bulabilirsiniz. Ayrıca, bir yöntemi global olarak değiştirmek için, bunu yalnızca tek bir yerden yapabilirsiniz
Tamir

0

Uzatma yöntemlerinin daha net kod yazılmasına yardımcı olduğunu düşünüyorum.

Arkadaşınızın önerdiği gibi, sınıfınızın içine yeni bir yöntem koymak yerine, onu ExtensionMethods ad alanına koyarsınız. Bu şekilde sınıfınıza mantıklı bir düzen duygusu verirsiniz. Sınıfınızla gerçekten doğrudan ilgilenmeyen yöntemler, onu karıştırmayacaktır.

Uzantı yöntemlerinin kodunuzu daha net ve daha uygun şekilde organize ettiğini düşünüyorum.


0

Editörünüzün / IDE'nizin otomatik tamamlama önerisini akıllıca yapmasını sağlar.


0

Onları html oluşturdukları için seviyorum. Çoğunlukla, tekrar tekrar kullanılan veya bir işlevin yararlı olduğu, ancak aksi takdirde programın akışını bozacağı yinelemeli olarak oluşturulan bölümler vardır.

        HTML_Out.Append("<ul>");
        foreach (var i in items)
            if (i.Description != "")
            {
                HTML_Out.Append("<li>")
                    .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description)
                    .Append("<div>")
                    .AppendImage(iconDir, i.Icon, i.Description)
                    .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("</div></li>");
            }

        return HTML_Out.Append("</ul>").ToString();

Ayrıca, bir nesnenin HTML çıktı genişletme yöntemleri için hazırlanacak özel mantığa ihtiyaç duyduğu durumlar da, sınıf içinde sunum ve mantığı karıştırmadan bu işlevi eklemenize izin verir.


0

Uzantı yöntemlerinin iç içe geçmiş genel bağımsız değişkenlerle eşleşmek için yararlı olduğunu buldum.

Kulağa biraz garip geliyor - ancak genel bir sınıfımız MyGenericClass<TList>olduğunu varsayalım ve TList'in kendisinin genel olduğunu biliyoruz (ör.List<T> ), her iki uzantı olmadan Listeden iç içe geçmiş 'T'yi çıkarmanın bir yolu olduğunu sanmıyorum. yöntemler veya statik yardımcı yöntemler. Elimizde yalnızca statik yardımcı yöntemlere sahipsek, bu (a) çirkin ve (b) bizi sınıfa ait olan işlevselliği harici bir konuma taşımaya zorlayacaktır.

Örneğin, bir demetteki türleri almak ve bunları bir yöntem imzasına dönüştürmek için uzantı yöntemlerini kullanabiliriz:

public class Tuple { }
public class Tuple<T0> : Tuple { }
public class Tuple<T0, T1> : Tuple<T0> { }

public class Caller<TTuple> where TTuple : Tuple { /* ... */ }

public static class CallerExtensions
{
     public static void Call<T0>(this Caller<Tuple<T0>> caller, T0 p0) { /* ... */ }

     public static void Call<T0, T1>(this Caller<Tuple<T0, T1>> caller, T0 p0, T1 p1) { /* ... */ }
}

new Caller<Tuple<int>>().Call(10);
new Caller<Tuple<string, int>>().Call("Hello", 10);

Bununla birlikte, bölme çizgisinin nerede olması gerektiğinden emin değilim - bir yöntem ne zaman bir uzatma yöntemi olmalı ve ne zaman statik bir yardımcı yöntem olmalıdır? Düşüncesi olan var mı?


0

Ekranımda giriş bölgelerim var ve tam türleri ne olursa olsun (metin kutuları, onay kutuları vb.) Tümü standart bir davranış uygulamalıdır. Her giriş bölgesi türü zaten belirli bir sınıftan (TextInputBox, vb.) Türetildiği için ortak bir temel sınıfı miras alamazlar.

Belki kalıtım hiyerarşisinde yukarı çıkarak WebControl gibi ortak bir ata bulabilirdim, ancak WebControl çerçeve sınıfını geliştirmedim ve ihtiyacım olanı açığa çıkarmıyor.

Uzatma yöntemiyle şunları yapabilirim:

1) WebControl sınıfını genişletin ve ardından tüm girdi sınıflarımda birleşik standart davranışımı elde edin

2) alternatif olarak, tüm sınıflarımın örneğin IInputZone gibi bir arabirimden türetilmesini sağlayın ve bu arabirimi yöntemlerle genişletin. Artık tüm giriş bölgelerimde arayüzle ilgili uzantı yöntemlerini arayabileceğim. Böylelikle bir tür çoklu kalıtım elde ettim çünkü girdi bölgelerim zaten birden fazla temel sınıftan türetildi.


0

Uzatma yöntemlerinin pek çok harika örneği var ... özellikle yukarıda belirtildiği gibi IEnumerables'da.

örneğin IEnumerable<myObject>, oluşturabilirim ve genişletme yöntemim varsaIEnumerable<myObject>

mylist List<myObject>;

... listeyi oluştur

mylist.DisplayInMyWay();

Uzantı Yöntemleri olmadan şunları çağırmak gerekir:

myDisplayMethod(myOldArray); // can create more nexted brackets.

başka bir harika örnek, flaşta Dairesel Bağlantılı Liste oluşturmaktır!

Bunun için kredi alabilirim!

uzatma yöntemlerini kullanan dairesel bağlantılı liste

Şimdi bunları birleştirin ve uzantı Yöntemlerini kullanarak kodu aşağıdaki gibi okur.

myNode.NextOrFirst().DisplayInMyWay();

ziyade

DisplayInMyWay(NextOrFirst(myNode)).

Genişletme Yöntemlerini Kullanma Daha temiz ve okunması daha kolay ve daha fazla nesne yönelimli. ayrıca şunlara çok yakın:

myNode.Next.DoSomething()

Bunu meslektaşınıza gösterin! :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.