Neden <T> Listesinden devralmıyorsunuz?


1399

Programlarımı planlarken, genellikle şöyle bir düşünce zinciriyle başlarım:

Bir futbol takımı sadece futbolcuların bir listesidir. Bu nedenle, şunu temsil etmeliyim:

var football_team = new List<FootballPlayer>();

Bu listenin sıralaması, oyuncuların kadroda listelendiği sırayı temsil eder.

Ancak daha sonra, takımların sadece oyuncu listesinin yanı sıra kaydedilmesi gereken başka özellikleri de olduğunu fark ediyorum. Örneğin, bu sezonun toplam skoru, mevcut bütçe, tekdüze renkler, stringtakımın adını temsil eden, vb.

Sonra düşünüyorum:

Tamam, bir futbol takımı sadece bir oyuncu listesi gibidir, ancak ek olarak, bir adı (a string) ve çalışan toplam skorları (an int) vardır. .NET, futbol takımlarını saklamak için bir sınıf sağlamaz, bu yüzden kendi sınıfımı yapacağım. En benzer ve ilgili mevcut yapı List<FootballPlayer>, bu yüzden miras alacağım:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

Ancak bir kılavuzun miras kalmamanız gerektiğini söylediğiList<T> ortaya çıkıyor . Bu rehberle iki açıdan tamamen kafam karıştı.

Neden olmasın?

Görünüşe göre Listperformans için bir şekilde optimize edilmiştir . Nasıl yani? Genişletirsem hangi performans sorunlarına neden olurum List? Tam olarak ne kırılacak?

Gördüğüm başka bir neden List, Microsoft tarafından sağlanan ve üzerinde herhangi bir kontrole sahip değilim , bu yüzden bir "genel API" açtıktan sonra daha sonra değiştiremiyorum . Ama bunu anlamak için mücadele ediyorum. Herkese açık bir API nedir ve neden ilgilenmeliyim? Şu anki projemde bu herkese açık API bulunmuyorsa ve bu olasılığa sahip değilse, bu kılavuzu göz ardı edebilir miyim? Eğer miras alırsamList ve genel bir API'ya ihtiyacım olduğu ortaya çıkarsa, ne tür zorluklar yaşayacağım?

Neden önemli? Liste bir listedir. Ne değişebilirdi? Neyi değiştirmek isteyebilirim?

Ve son olarak, Microsoft benim miras almamı istemiyorsa List, neden sınıfı yapmadılarsealed ?

Başka ne kullanmam gerekiyor?

Görünüşe göre, özel koleksiyonlar için Microsoft, Collectionbunun yerine genişletilmesi gereken bir sınıf sağlamıştır List. Ancak bu sınıf çok çıplaktır ve örneğinAddRange , pek çok yararlı şeyi yoktur . jvitor83'ün cevabı , bu belirli yöntem için bir performans mantığı sağlar, ancak bir yavaş nasıl hayırdanAddRange daha iyi değil AddRange?

Devralmak, Collectiondevralmaktan çok daha fazla iştir Listve hiçbir fayda görmüyorum. Şüphesiz Microsoft bana hiçbir sebepten ötürü fazladan iş yapmamı söylemedi, bu yüzden bir şekilde bir şeyi yanlış anlıyormuşum gibi hissetmeme yardım edemiyorum ve kalıtım Collectionaslında problemim için doğru çözüm değil.

Uygulama gibi öneriler gördüm IList. Sadece hayır. Bu bana hiçbir şey kazandıran düzinelerce boyunda kod satırı.

Son olarak, bazıları bir Listşeye sarmayı önerir :

class FootballTeam 
{ 
    public List<FootballPlayer> Players; 
}

Bununla birlikte iki tane sorun var:

  1. Kodumu gereksiz yere ayrıntılı yapar. Şimdi my_team.Players.Countsadece yerine çağırmalıyım my_team.Count. Neyse ki, C # ile indekslemeyi şeffaf hale getirmek ve dahili tüm yöntemleri iletmek için indeksleyicileri tanımlayabilirim List... Ama bu çok fazla kod! Tüm bu işler için ne alacağım?

  2. Sadece sade bir anlam ifade etmiyor. Bir futbol takımının oyuncu listesi "yoktur". Bu ise oyuncuların listesi. "John McFootballer SomeTeam oyuncularına katıldı" demiyorsunuz. "John SomeTeam'e katıldı" diyorsunuz. "Bir dizenin karakterlerine" bir harf eklemezsiniz, bir dizeye bir harf eklersiniz. Bir kitaplığın kitaplarına kitap eklemezsiniz, kitaplığa bir kitap eklersiniz.

"Kaputun altında" olan şeyin "Y'nin iç listesine X eklenmesi" olduğu söylenebilir, ancak bu dünya hakkında oldukça sezgisel bir düşünme yolu gibi görünüyor.

Sorum (özetlendi)

"Mantıksal" ( "insan beynine" demek olduğunu) sadece bir veri yapısını temsil doğru C # yolu nedir listait thingsbirkaç çan ve ıslık ile?

Her List<T>zaman kabul edilemez mi? Ne zaman kabul edilebilir? Neden / neden olmasın? Bir programcı, miras alıp almamaya karar verirken neleri dikkate almalıdır List<T>?


90
Yani, sorunuz gerçekten mirasın ne zaman kullanılacağıdır (bir Kare-Dikdörtgendir, çünkü genel bir Dikdörtgen istendiğinde bir Kare sağlamak her zaman mantıklıdır) ve kompozisyon ne zaman (FootballTeam has-a FootballPlayers Listesi vardır) bir isim gibi "temel" olan diğer mülklere)? Basit bir Liste beklerken kodun başka bir yerinde bir FootballTeam'i geçtiğinizde diğer programcıların kafası karışır mı?
Flight Odyssey

77
Bir futbol takımının çıplak yerine oyuncu sözleşmelerinin bir listesini SAHİP olan bazı ek özelliklere sahip oyuncuların listesi olan bir iş şirketi olduğunu söylesem ne olur?
STT LCU

75
Gerçekten çok basit. Bir futbol takımı oyuncu listesi midir? Açıkçası hayır, çünkü dediğiniz gibi, başka alakalı özellikler var. Bir futbol takımının oyuncu listesi var mı? Evet, bu yüzden bir liste içermelidir. Bunun dışında, kompozisyon genel olarak kalıtım için tercih edilir, çünkü daha sonra değiştirmek daha kolaydır. Kalıtım ve kompozisyonu aynı anda mantıklı bulursanız, kompozisyonla gidin.
Tobberoth

45
@FlightOdyssey: Dikdörtgen alan, yüksekliğini yarıya indiren ve genişliğini iki katına çıkaran SquashRectangle yönteminiz olduğunu varsayalım. Şimdi bir dikdörtgen içinde geçmesi olur 4x4 olmak ancak tip dikdörtgenin olduğunu ve 4x4 bir kare. SquashRectangle çağrıldıktan sonra nesnenin boyutları nelerdir? Dikdörtgen için açıkça 2x8'dir. Kare 2x8 ise, artık bir kare değildir; 2x8 değilse, aynı şekildeki aynı işlem farklı bir sonuç verir. Sonuç bir değişken kare olmasıdır değil dikdörtgenin bir tür.
Eric Lippert

29
@Gangnus: İfadeniz sadece yanlış; üst sınıfın üst kümesi olan işlevselliğe sahip olmak için gerçek bir alt sınıf gereklidir . Bir stringHer şey yapmasını gerektiren durumlar objectyapabileceği daha .
Eric Lippert

Yanıtlar:


1566

Burada bazı iyi cevaplar var. Onlara aşağıdaki noktaları eklerdim.

"Mantıksal" (yani "insan zihnine") sadece birkaç çan ve ıslık olan şeylerin bir listesi olan bir veri yapısını temsil etmenin doğru C # yolu nedir?

Futbolun varlığını bilen bilgisayar programcısı olmayan on kişiden boş alanı doldurmasını isteyin:

Bir futbol takımı _____________

Did kimse diyelim "Birkaç çan ve ıslık ile futbolcuların listesi" ya hepsi diyelim "spor takımı" veya "kulüp" ya da "örgüt" mi? Bir futbol takımının belirli bir tür oyuncu listesi olduğu fikriniz sadece sizin ve sadece sizin zihninizde.

List<T>bir mekanizmadır . Futbol takımı bir iş nesnesidir - yani, programın ticari alanındaki bazı kavramları temsil eden bir nesnedir . Bunları karıştırmayın! Bir futbol takımı bir tür takımdır; o bir sahip bir liste, liste oyuncuların listesidir . Liste belirli bir oyuncu listesi değildir . Bir liste olduğu oyuncuların listesi. Yani adlı bir özellik yapmak Rosterbir olduğunu List<Player>. Ve ReadOnlyList<Player>bir futbol takımını bilen herkesin oyuncuları listeden silebileceğine inanmadığınız sürece bunu yapın.

Her List<T>zaman kabul edilemez mi?

Kime kabul edilemez? Ben mi? Hayır.

Ne zaman kabul edilebilir?

Eğer bir mekanizma bina yapıyorsanız uzanır List<T>mekanizması .

Bir programcı, miras alıp almamaya karar verirken neleri dikkate almalıdır List<T>?

Bir mekanizma mı yoksa bir iş nesnesi mi oluşturuyorum?

Ama bu çok fazla kod! Tüm bu işler için ne alacağım?

List<T>Elli kez üzerinden ilgili üyeler için yönlendirme yöntemleri yazmanızın isteneceği sorunuzu yazmak için daha fazla zaman harcadınız . Açık bir şekilde ayrıntıdan korkmuyorsunuz ve burada çok az miktarda koddan bahsediyoruz; bu birkaç dakikalık bir çalışmadır.

GÜNCELLEME

Biraz daha düşündüm ve bir futbol takımını oyuncu listesi olarak modellememenin bir başka nedeni daha var. Aslında bir futbol takımını oyuncuların bir listesine sahip olarak modellemek kötü bir fikir olabilir . Bir takımın bir oyuncu listesi olması / sahip olması sorunu, sahip olduğunuz şeyin takımın bir anda anlık görüntüsü olmasıdır . Bu sınıf için iş durumunuzun ne olduğunu bilmiyorum, ancak bir futbol takımını temsil eden bir sınıfım olsaydı, "2003 ve 2013 arasında kaç Seahawks oyuncusu sakatlık nedeniyle oyunları kaçırdı?" ya da "Daha önce başka bir takımda oynayan Denver oyuncuları geçen yıla göre en büyük artışı gerçekleştirdi?" veya " Piggers bu yıl sonuna kadar gitti mi? "

Beni sıra modellenecek olduğunu, bir futbol takımı gibi görünüyor tarihsel gerçekler bütünü böyle önden ve- bir oyuncu geçerli oynatıcı liste muhtemelen olmalıdır önemli bir olgudur Açıkçası vb emekli, işe yaralandı olduğu gibi ancak bu nesne ile daha tarihsel bir perspektif gerektiren başka ilginç şeyler olabilir.


84
Diğer cevaplar çok yardımcı olmasına rağmen, bunun en kaygılarımı doğrudan ele aldığını düşünüyorum. Kod miktarını abartmak için, sonunda o kadar iş olmadığını doğru, ama neden dahil olduğumu anlamadan benim programa bazı kod eklediğinizde çok kolay karıştı.
Superbest

128
@Superbest: Yardımcı olabildiğime sevindim! Bu şüpheleri dinlemekte haklısınız; neden bazı kodları yazdığınızı anlamıyorsanız ya anlayın ya da anladığınız farklı bir kod yazın.
Eric Lippert

48
@Mehrdad Ama optimizasyonun altın kurallarını unutmuş görünüyorsunuz: 1) yapmayın, 2) henüz yapmayın ve 3) neyin optimizasyon gerektiğini göstermek için performans profilleri yapmadan bunu yapmayın.
AJMansfield

107
@Mehrdad: Dürüst olmak gerekirse, uygulamanız sanal yöntemlerin performans yükünü önemsemenizi gerektiriyorsa, herhangi bir modern dil (C #, Java, vb.) Sizin için bir dil değildir. Burada, aynı anda çocukken oynadığım her arcade oyununun simülasyonunu çalıştırabilecek bir dizüstü bilgisayarım var . Bu, burada ve orada bir dolaylama seviyesi hakkında endişelenmeme izin vermiyor.
Eric Lippert

44
@ Superbest: Şimdi kesinlikle spektrumun "kompozisyon kalıtım değil" sonundayız. Bir roket roket parçalarından oluşur . Bir roket "özel bir parça listesi" değildir! Size daha da kesmenizi öneririm; neden olarak karşıt bir liste, IEnumerable<T>- bir dizi ?
Eric Lippert

161

Vay canına, yayınınızda birçok soru ve puan var. Microsoft'tan aldığınız nedenlerin çoğu tam olarak yerinde. Hakkında her şeyle başlayalımList<T>

  • List<T> son derece optimize edilmiştir. Ana kullanımı bir nesnenin özel bir üyesi olarak kullanılmalıdır.
  • Bazen bir dostça adı olan bir sınıf oluşturmak isteyebilirsiniz çünkü Microsoft mühürlerim vermedi: class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... }. Şimdi yapmak kadar kolay var list = new MyList<int, string>();.
  • CA1002: Genel listeleri ortaya koymayın : Temel olarak, bu uygulamayı tek geliştirici olarak kullanmayı planlasanız bile, iyi kodlama uygulamaları ile geliştirilmeye değer, böylece size ve ikinci doğaya aşılanırlar. IList<T>Dizine eklenmiş bir listeye sahip olmak için herhangi bir tüketiciye ihtiyacınız varsa , listeyi yine de gösterebilirsiniz . Bu, daha sonra bir sınıf içindeki uygulamayı değiştirmenizi sağlar.
  • Microsoft Collection<T>genel bir kavram olduğu için çok genel bir isim yaptı ... isim her şeyi söylüyor; sadece bir koleksiyon. Gibi daha hassas versiyonu vardır SortedCollection<T>, ObservableCollection<T>, ReadOnlyCollection<T>uygulamak her biri, vb IList<T>ama değil List<T> .
  • Collection<T>üyelerin (örn. Ekle, Kaldır vb.) sanal oldukları için geçersiz kılınmasına olanak tanır. List<T>değil.
  • Sorunuzun son kısmı açık. Bir Futbol takımı sadece bir oyuncu listesinden daha fazlasıdır, bu yüzden o oyuncu listesini içeren bir sınıf olmalıdır. Kompozisyon ve Miras düşünün . Bir Futbol takımı vardır oyuncuların bir listesini (bir liste), bu oyuncuların listesi değildir.

Bu kodu yazıyorsanız sınıf muhtemelen böyle bir şey olurdu:

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    // Yes. I used LINQ here. This is so I don't have to worry about
    // _roster.Length vs _roster.Count vs anything else.
    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

6
" Yayınınızda soruların tamamı var " - Tamamen kafam karışmıştı demiştim. @ Readonly'nin sorusuna bağladığınız için teşekkürler, şimdi sorumun büyük bir bölümünün daha genel Kompozisyon ve Kalıtım probleminin özel bir örneği olduğunu görüyorum.
Superbest

3
@Brian: Takma ad kullanarak genel bir öğe oluşturamamanız dışında, tüm türler somut olmalıdır. Benim örnekte, List<T>kullanımını ve beyanı basitleştirmek için generic'leri kullanan özel iç sınıf olarak uzatılır List<T>. Uzantı sadece olsaydı, class FootballRoster : List<FootballPlayer> { }evet onun yerine bir takma ad kullanabilirdim. Ek üye / işlevsellik ekleyebileceğinizi belirtmek gerekir, ancak önemli üyelerini geçersiz kılamayacağınız için sınırlıdır List<T>.
Benim

6
Bu, Programcılar olsaydı.SE, mevcut cevap oyları aralığına katılırdım, ancak bir SO yayını olduğu için, bu cevap en azından kesin genel olsa bile C # (.NET) ile ilgili sorunlara cevap vermeye çalışır. TASARIM SORUNLARI.
Mark Hurd

7
Collection<T> allows for members (i.e. Add, Remove, etc.) to be overriddenŞimdi bu Listeden miras almamak için iyi bir neden. Diğer cevapların çok fazla felsefesi var.
Navin

10
@ my: Ben bir soru bir dedektif olduğu için soruları "çevirmek" anlamına geldiğinden şüpheleniyorum.
batty

152
class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal;
}

Önceki kod şu anlama gelir: sokaktan futbol oynayan bir grup adam ve bir isimleri var. Gibi bir şey:

Futbol oynayanlar

Her neyse, bu kod (cevabımdan)

public class FootballTeam
{
    // A team's name
    public string TeamName; 

    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

Araçlar: Bu, yönetimi, oyuncuları, yöneticileri vb. Olan bir futbol takımıdır.

Manchester United ekibinin üyelerini (ve diğer özelliklerini) gösteren resim

Mantığınız resimlerde böyle sunuluyor…


9
İkinci örneğinizin bir futbol takımı değil, bir Futbol Kulübü olmasını beklerim. Bir futbol kulübünün yöneticileri ve idarecileri vb. Vardır. Bu kaynaktan : "Bir futbol takımı, oyuncuya ... Bu tür takımlar, rakip bir takıma karşı bir maçta oynamak, bir futbol kulübünü temsil etmek için seçilebilir ... ". Bu yüzden bir takım yani Bence olup oyuncuların listesi (oyuncuların ya da belki daha doğru bir koleksiyon).
Ben

3
Daha optimize edilmişprivate readonly List<T> _roster = new List<T>(44);
SiD

2
Ad System.Linqalanını içe aktarmak gerekir .
Davide Cannizzo

1
Görseller için: +1
V.7

115

Bu, bileşime ve mirasa klasik bir örnektir .

Bu özel durumda:

Takım, davranış eklenmiş oyuncuların bir listesi mi

veya

Takım, oyuncuların listesini içeren kendine ait bir nesne mi?

Listeyi genişleterek kendinizi çeşitli yollarla sınırlandırmış olursunuz:

  1. Erişimi kısıtlayamazsınız (örneğin, listeyi değiştiren kişileri durdurma). İster ihtiyacınız olsun ister istemeyin, tüm Liste yöntemlerini alırsınız.

  2. Başka şeylerin listelerini de almak istiyorsanız ne olur? Örneğin, takımların koçları, yöneticileri, taraftarları, ekipmanları vb. Vardır. Bunlardan bazıları kendi başlarına listeler olabilir.

  3. Kalıtım seçeneklerinizi sınırlarsınız. Örneğin, genel bir Team nesnesi oluşturmak ve daha sonra bundan devralan BaseballTeam, FootballTeam vb. Listeden miras almak için Team'den miras almanız gerekir, ancak bu, tüm çeşitli takım türlerinin aynı kadroyu aynı şekilde uygulamaya zorlandığı anlamına gelir.

Kompozisyon - nesnenizin içinde istediğiniz davranışı veren bir nesne dahil.

Kalıtım - nesneniz, istediğiniz davranışa sahip nesnenin bir örneği olur.

Her ikisinin de kullanımları vardır, ancak bu bileşimin tercih edildiği açık bir durumdur.


1
Bir Liste için 2. ayrıntılı şekilde anlatıldığı bu mantıklı değil var-bir listesi.
Cruncher

İlk olarak, sorunun kompozisyon ve kalıtımla ilgisi yoktur. Cevap OP'nin daha spesifik bir liste uygulamak istemediğidir, bu yüzden List <> 'i genişletmemelidir. Ben büküldüm çünkü stackoverflow üzerinde çok yüksek puanları var ve bunu açıkça bilmeli ve insanlar söylediklerinize güveniyor, bu yüzden şimdi kaldırılmış ve fikri karışmış 55 kişi ya bir şekilde ya da diğeri inşa etmek için uygun bir sistem ama açıkça değil! # no-rage
Mauro Sampietro

6
@sam Listenin davranışını istiyor. İki seçeneği vardır, listeyi uzatabilir (miras) veya nesnesinin içine bir liste (kompozisyon) ekleyebilir. Belki 55 kişinin yanlış olması yerine sorunun veya cevabın bir kısmını yanlış anladınız ve değil mi? :)
Tim B

4
Evet. Bu sadece bir süper ve sub ile yozlaşmış bir durum ama onun özel sorusu için daha genel bir açıklama yapıyorum. Sadece bir takımı olabilir ve her zaman bir listesi olabilir ama seçim listeyi devralmak veya onu dahili bir nesne olarak dahil etmektir. Birden fazla takım türünü (futbol. Kriket. Vb) dahil etmeye başladığınızda ve sadece oyuncuların bir listesinden daha fazlası olmaya başlarlar, tam kompozisyona ve miras sorusuna nasıl sahip olduğunuzu görürsünüz. Büyük resme bakmak, yanlış seçimlerden erken kaçınmak için bu gibi durumlarda önemlidir, bu da daha sonra yeniden düzenleme yapmak anlamına gelir.
Tim B

3
OP, Kompozisyon istemedi, ancak kullanım durumu, 4 nesne yönelimli programlama ilkelerinden birinin kırıldığı, yanlış kullanıldığı veya tam olarak anlaşılmadığı X: Y sorununun açık bir örneğidir. Daha açık yanıt, Yığın veya Kuyruk gibi özel bir koleksiyon yazmak (kullanım durumuna uymayan) veya kompozisyonu anlamaktır. Bir futbol takımı futbolcuların listesi DEĞİLDİR. OP'nin açıkça istediği veya istemediği önemli değil, Soyutlama, Kapsülleme, Kalıtım ve Polimorfizm'i anlamadan cevabı anlamayacaklar, ergo, X: Y amok
Julia McGuigan

67

Herkesin işaret ettiği gibi, bir oyuncu takımı bir oyuncu listesi değildir. Bu hata her yerde, belki de çeşitli uzmanlık seviyelerinde birçok insan tarafından yapılır. Çoğu zaman, bu durumda olduğu gibi, sorun ince ve bazen çok iğrençtir. Bu tür tasarımlar kötü çünkü bunlar Liskov İkame İlkesini ihlal ediyor . İnternette bu kavramı açıklayan birçok iyi makale bulunmaktadır; örneğin, http://en.wikipedia.org/wiki/Liskov_substitution_principle

Özetle, sınıflar arasında bir Ebeveyn / Çocuk ilişkisinde korunması gereken iki kural vardır:

  • bir Çocuk, Ebeveyn'i tamamen tanımlayandan daha az karakteristik gerektirmemelidir.
  • bir Ebeveyn, Çocuğu tamamen tanımlayana ek olarak herhangi bir özellik gerektirmemelidir.

Başka bir deyişle, bir Ebeveyn bir çocuğun gerekli bir tanımıdır ve bir çocuk bir Ebeveynin yeterli bir tanımıdır.

İşte bir çözüm üzerinden düşünmek ve böyle bir hatadan kaçınmaya yardımcı olacak yukarıdaki prensibi uygulamak için bir yol. Bir üst sınıfın tüm işlemlerinin türetilmiş sınıf için hem yapısal hem de anlamsal olarak geçerli olup olmadığını doğrulayarak, kişi hipotezini test etmeliyiz.

  • Bir futbol takımı futbolcuların bir listesi midir? (Bir listenin tüm özellikleri aynı anlamda bir ekibe uygulanır mı?)
    • Bir ekip homojen varlıklardan oluşan bir koleksiyon mu? Evet, takım bir Oyuncu topluluğudur
    • Oyuncuların dahil olma sırası takımın durumunu tanımlayıcı mı ve takım açıkça değiştirilmedikçe sıralamanın korunmasını sağlıyor mu? Hayır ve Hayır
    • Oyuncuların takımdaki sıralı konumlarına göre dahil edilmesi / düşürülmesi bekleniyor mu? Hayır

Gördüğünüz gibi, bir listenin yalnızca ilk özelliği bir ekibe uygulanabilir. Dolayısıyla bir takım liste değildir. Liste, takımınızı nasıl yönettiğinizin uygulama ayrıntısı olacaktır, bu nedenle yalnızca oyuncu nesnelerini depolamak ve Team sınıfı yöntemleriyle manipüle etmek için kullanılmalıdır.

Bu noktada, bir Takım sınıfının, bence, bir Liste kullanılarak uygulanmaması gerektiğini belirtmek isterim; çoğu durumda bir Set data yapısı (örneğin HashSet) kullanılarak uygulanmalıdır.


8
Set karşı listesinde güzel yakalamak. Bu çok sık yapılan bir hata gibi görünüyor. Bir koleksiyonda bir öğenin yalnızca bir örneği olabiliyorsa, uygulama için bir tür küme veya sözlük tercih edilmelidir. Aynı isimli iki oyuncudan oluşan bir ekibe sahip olmak sorun değil. Bir oyuncunun aynı anda iki kez dahil edilmesi uygun değildir.
Fred Mitchell

1
Bazı iyi noktalar için +1, ancak "veri yapısı yararlı olmaktan fazlasını yapar mı" yerine "bir veri yapısının faydalı olan her şeyi (ideal olarak kısa ve uzun vadeli) makul bir şekilde destekleyebilir mi?" Sorusunu sormak daha önemlidir.
Tony Delroy

1
@TonyD Yetiştirdiğim noktalar, "veri yapısının faydalı olandan daha fazlasını yapıp yapmadığını" kontrol etmesi gerektiğidir. "Üst veri yapısının, Çocuk sınıfının ima edebileceği davranışla alakasız, anlamsız veya sezgisel bir şey yapıp yapmadığı" kontrol edilir.
Satyan Raina

1
@TonyD Pek çok durumda negatif testlerde başarısız olacağı için bir Ebeveynden türetilmiş alakasız özelliklere sahip olmakla ilgili bir sorun vardır. Bir programcı Human {eat (); Çalıştırmak(); bir Gorilla'dan yaz ();} {eat (); Çalıştırmak(); swing ();} salıncak yapabilme özelliğine sahip bir insanda yanlış bir şey olmadığını düşünmek. Ve sonra bir oyun dünyasında, insanınız aniden ağaçların üzerinde sallanarak sözde tüm engelleri atlamaya başlar. Açıkça belirtilmedikçe, pratik bir İnsan sallanamaz. Böyle bir tasarım,
api'yi

1
@TonyD Player sınıfının HashSet'ten de türetilmesini önermiyorum. Player sınıfının 'çoğu durumda' Kompozisyon yoluyla bir HashSet kullanılarak uygulanması gerektiğini ve tasarım düzeyinde değil, tamamen uygulama düzeyinde bir ayrıntı olduğunu öneriyorum (bu yüzden cevabımın bir sidenoteu olarak bahsettim). Böyle bir uygulama için geçerli bir gerekçe varsa, bir liste kullanılarak çok iyi uygulanabilir. Sorunuzu cevaplamak için, O (1) anahtarını aramak gerekli midir? Hayır. Bu nedenle, bir Oyuncu bir HashSet'ten de Genişletilmemelidir.
Satyan Raina

50

Ya FootballTeamana ekiple birlikte yedek ekip varsa ?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
}

Bunu nasıl modellersiniz?

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

İlişki açıkça bir ve değil a .

veya RetiredPlayers?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
    List<FootballPlayer> RetiredPlayers { get; set; }
}

Genel bir kural olarak, bir koleksiyondan devralmak istiyorsanız, sınıfı adlandırın SomethingCollection.

Senin mu SomethingCollectionsemantik mantıklı? Bunu yalnızca türünüz bir koleksiyonsa yapın Something.

Durumunda FootballTeamkulağa doğru gelmiyor. A Team, a'dan daha fazlasıdır Collection. A Team, diğer cevapların işaret ettiği gibi antrenörler, eğitmenler vb.

FootballCollectionfutbol topluluğuna veya belki de futbol gereçlerinin topluluğuna benziyor. TeamCollection, ekiplerden oluşan bir koleksiyon.

FootballPlayerCollectionList<FootballPlayer>gerçekten yapmak istediğinizde miras kalan bir sınıf için geçerli bir isim olacak bir oyuncu koleksiyonu gibi geliyor .

Gerçekten mi List<FootballPlayer> başa çıkmak için mükemmel bir türüdür. Belki IList<FootballPlayer>bir yöntemle iade ediyorsanız.

Özetle

Kendine sor

  1. X bir Y? ya Has X aY ?

  2. Sınıf isimlerim ne oldukları anlamına mı geliyor?


Her oyuncunun tipi ya ait olarak sınıflandırmak istiyorsanız DefensivePlayers, OffensivePlayersya OtherPlayersbir beklediği kod tarafından kullanılabilecek bir tür olması, bu meşru yararlı olabilir List<Player>ama aynı zamanda dahil üyeleri DefensivePlayers, OffsensivePlayersveya SpecialPlayersÇeşidi IList<DefensivePlayer>, IList<OffensivePlayer>ve IList<Player>. Ayrı listeleri önbelleğe almak için ayrı bir nesne kullanılabilir, ancak bunları ana liste ile aynı nesne içinde kapsüllemek daha temiz görünecektir [bir liste numaralandırıcısının geçersiz kılınmasını kullanın ...
supercat

... ana listenin değişmiş olması ve alt listelerin bir sonraki erişimlerinde yeniden oluşturulması gerektiği için bir ipucu olarak].
supercat

2
Belirtilen noktaya katılırken, birisinin tasarım tavsiyesi verdiğini ve bir iş nesnesindeki bir kamu alıcısı ve belirleyicisiyle somut bir Liste <T> ortaya koymasını önermek ruhumu parçalıyor :(
sara

38

Tasarım> Uygulama

Hangi yöntemleri ve özellikleri ortaya koyduğunuz bir tasarım kararıdır. Hangi temel sınıfı devralmış olduğunuz bir uygulama ayrıntısıdır. Birincisine geri adım atmaya değdiğini hissediyorum.

Nesne, veri ve davranışın bir koleksiyonudur.

Yani ilk sorularınız:

  • Bu nesne oluşturduğum modelde hangi verileri içeriyor?
  • Bu nesne bu modelde hangi davranışı sergiliyor?
  • Bu gelecekte nasıl değişebilir?

Kalıtımın "isa" (a) ilişkisi olduğunu, kompozisyonun ise "hasa" ilişkisi olduğunu ima ettiğini unutmayın. Uygulamanız geliştikçe işlerin nereye gidebileceğini göz önünde bulundurarak durumunuz için doğru olanı seçin.

Somut tiplerde düşünmeden önce arayüzlerde düşünmeyi düşünün, çünkü bazı insanlar beynini bu şekilde "tasarım moduna" koymayı daha kolay buluyorlar.

Bu, günlük kodlamada herkesin bilinçli olarak bu seviyede yaptığı bir şey değildir. Ancak bu tür bir konuyu düşünüyorsanız, tasarım sularına giriyorsunuz. Bunun farkında olmak özgürleştirici olabilir.

Tasarım Özelliklerini Göz önünde bulundurun

MSDN veya Visual Studio'da <T> ve IList <T> Listelerine göz atın. Hangi yöntemleri ve özellikleri gösterdiklerine bakın. Bu yöntemlerin hepsi sizin görüşünüze göre birinin FootballTeam'e yapmak isteyeceği bir şey gibi mi gözüküyor?

FootballTeam.Reverse () size mantıklı geliyor mu? FootballTeam.ConvertAll <TOutput> () istediğiniz bir şeye benziyor mu?

Bu hileli bir soru değil; cevap gerçekten "evet" olabilir. <Player> Listesini veya IList <Player> Listesini uygular / devralırsanız, bunlara takılı kalırsınız; modeliniz için idealse, yapın.

Evet'e karar verirseniz, bu mantıklıdır ve nesnenizin bir koleksiyon / oyuncu listesi (davranış) olarak ele alınmasını istersiniz ve bu nedenle ICollection veya IList'i uygulamak istersiniz, bunu yapın. notionally:

class FootballTeam : ... ICollection<Player>
{
    ...
}

Nesnenin bir koleksiyon / oyuncu listesi (veri) içermesini ve bu nedenle koleksiyonun veya listenin bir mülk veya üye olmasını istiyorsanız, elbette bunu yapın. notionally:

class FootballTeam ...
{
    public ICollection<Player> Players { get { ... } }
}

İnsanların saymak, onlara eklemek veya kaldırmak yerine sadece oyuncu setini numaralandırmasını istediğinizi düşünebilirsiniz. IEnumerable <Player> dikkate alınması gereken mükemmel bir seçenektir.

Bu arayüzlerin hiçbirinin modelinizde hiç kullanışlı olmadığını düşünebilirsiniz. Bu daha az olasıdır (IEnumerable <T> birçok durumda yararlıdır) ancak yine de mümkündür.

Size bunlardan birinin her durumda kategorik ve kesin olarak yanlış olduğunu söylemeye çalışan herkes yanlış yönlendirilir. Size bunu söylemeye çalışan herkes , her durumda kategorik ve kesin olarak doğrudur .

Uygulamaya Geç

Veri ve davranışa karar verdikten sonra, uygulama hakkında bir karar verebilirsiniz. Bu, kalıtım veya kompozisyon yoluyla hangi somut sınıflara bağımlı olduğunuzu içerir.

Bu büyük bir adım olmayabilir ve insanlar genellikle kafanızda bir veya iki saniyede geçmek ve yazmaya başlamak mümkün olduğundan tasarım ve uygulamayı birbirine bağlar.

Düşünce Deneyi

Yapay bir örnek: diğerlerinin de belirttiği gibi, bir takım her zaman sadece bir oyuncu topluluğu değildir. Takım için bir maç skorları koleksiyonu tutuyor musunuz? Takım, modelinizle kulüple değiştirilebilir mi? Eğer öyleyse ve takımınız bir oyuncu koleksiyonuysa, belki de bir personel koleksiyonu ve / veya bir puanlar topluluğudur. Sonra ile sonuçlanır:

class FootballTeam : ... ICollection<Player>, 
                         ICollection<StaffMember>,
                         ICollection<Score>
{
    ....
}

Tasarım, buna rağmen, C # 'da bu noktada, <T> Listesinden devralınarak bunları uygulayamazsınız, çünkü C # "yalnızca" tek mirası destekler. (Bu malarky'yi C ++ 'da denediyseniz, bunu iyi bir şey olarak düşünebilirsiniz.) Bir koleksiyonu miras yoluyla ve bir koleksiyonu kompozisyon yoluyla uygulamak muhtemelen kirli hissedecektir . Ve ILIst <Player> .Count ve IList <StaffMember> .Count vb. Uygulamalarını açıkça uygulamadığınız sürece Count gibi özellikler kullanıcılar için kafa karıştırıcı hale gelir ve sonra kafa karıştırıcı olmaktan ziyade sadece acı verir. Bunun nereye gittiğini görebilirsiniz; bağırsak hissi bu caddeyi düşünürken size bu yönde gitmenin yanlış hissettirdiğini söyleyebilir (ve doğru veya yanlış, meslektaşlarınız da bu şekilde uyguladıysanız!)

Kısa Cevap (Çok Geç)

Koleksiyon sınıflarından miras almamaya ilişkin kılavuz C # 'a özgü değildir, bunu birçok programlama dilinde bulabilirsiniz. Kanuna değil, bilgeliğe kavuşur. Bunun bir nedeni, pratikte kompozisyonun anlaşılabilirlik, uygulanabilirlik ve sürdürülebilirlik açısından kalıtımdan sık sık kazandığı düşünülmektedir. Özetle derinlemesine olmadığınız sürece, özellikle de zaman geçtikçe ve koddaki nesnelerin kesin verileri ve davranışları olmadıkça, yararlı ve tutarlı "hasa" ilişkileri bulmak, yararlı ve tutarlı "hasa" ilişkileri bulmak, gerçek dünya / etki alanı nesneleriyle daha yaygındır. değiştirir. Bu, her zaman toplama sınıflarından miras almayı dışlamanıza neden olmamalıdır; ama müstehcen olabilir.


34

Her şeyden önce, kullanılabilirlik ile ilgilidir. Kalıtım kullanırsanız, Teamsınıf yalnızca nesne yönetimi için tasarlanmış davranışı (yöntemleri) ortaya koyar. Örneğin, AsReadOnly()ya da CopyTo(obj)yöntemler takım nesnesi için bir anlam ifade etmiyor. AddRange(items)Yöntem yerine muhtemelen daha açıklayıcı bir AddPlayers(players)yöntem istiyorsunuz .

LINQ kullanmak istiyorsanız, ICollection<T>veya gibi genel bir arayüz uygulamak IEnumerable<T>daha mantıklı olacaktır.

Belirtildiği gibi, kompozisyon bu konuda doğru yol. Özel değişken olarak bir oyuncu listesi uygulayın.


30

Bir futbol takımı futbolcuların bir listesi değildir . Bir futbol takımı oluşmaktadır bir listesinden !

Bu mantıksal olarak yanlış:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

ve bu doğrudur:

class FootballTeam 
{ 
    public List<FootballPlayer> players
    public string TeamName; 
    public int RunningTotal 
}

19
Bu nedenini açıklamıyor . OP, diğer birçok programcının bu şekilde hissettiğini biliyor, bunun neden önemli olduğunu anlamıyor. Bu sadece söz konusu olan bilgileri sağlıyor.
14'te Servis

2
Ben de ilk düşündüğüm şey, verilerinin nasıl yanlış bir şekilde modellenmiş olduğudur. Bu nedenle, veri modeliniz yanlış.
rball

3
Neden listeden devralmıyorsunuz? Çünkü daha özel bir liste istemiyor.
Mauro Sampietro

2
İkinci örneğin doğru olduğunu düşünmüyorum. Devleti açığa vuruyorsunuz ve böylece kapsüllemeyi kırıyorsunuz. Devlet nesneye gizlenmeli / içlenmeli ve bu durumu değiştirmenin yolu genel yöntemlerle olmalıdır. Tam olarak hangi yöntemler iş gereksinimlerinden belirlenecek bir şeydir, ama "şu an buna ihtiyacım var" -bazen olmalı, "bilmiyorum bir süre bilmiyorum" -bazım. API'nizi sıkı tutun ve zamandan ve emekten tasarruf edersiniz.
sara

1
Kapsülleme sorunun konusu değildir. Bu türün daha spesifik bir şekilde davranmasını istemiyorsanız, bir türü genişletmemeye odaklanıyorum. Bu alanlar diğer dillerde c # ve get / set yöntemlerinde
otoproperty olmalıdır

28

Bu koşullara bağlıdır

Takımınızı bir oyuncu listesi olarak gördüğünüzde, bir ayak topu takımının "fikrini" bir yöne yansıtırsınız: "Ekibi" sahada gördüğünüz kişilere indirgersiniz. Bu projeksiyon sadece belirli bir bağlamda doğrudur. Farklı bir bağlamda, bu tamamen yanlış olabilir. Ekibin sponsoru olmak istediğinizi düşünün. Bu yüzden ekibin yöneticileri ile konuşmalısınız. Bu bağlamda ekip, yöneticiler listesine yansıtılır. Ve bu iki liste genellikle çok fazla örtüşmüyor. Diğer bağlamlar, eski oyunculara karşı güncel olanlardır.

Belirsiz anlambilim

Yani bir takımı oyuncularının bir listesi olarak görmenin sorunu, anlamsallığının bağlama bağlı olması ve bağlam değiştiğinde genişletilememesidir. Ek olarak, hangi bağlamı kullandığınızı ifade etmek zordur.

Sınıflar genişletilebilir

Yalnızca bir üyeli (örneğin IList activePlayers) bir sınıf kullandığınızda, bağlamı netleştirmek için üyenin adını (ve ayrıca yorumunu) kullanabilirsiniz. Ek bağlamlar olduğunda, yalnızca ek bir üye eklersiniz.

Sınıflar daha karmaşık

Bazı durumlarda fazladan bir sınıf oluşturmak aşırıya kaçabilir. Her sınıf tanımının sınıf yükleyici aracılığıyla yüklenmesi gerekir ve sanal makine tarafından önbelleğe alınır. Bu, çalışma zamanı performansına ve belleğe mal olur. Çok özel bir bağlamınız olduğunda, bir futbol takımını bir oyuncu listesi olarak kabul etmek uygun olabilir. Ama bu durumda, gerçekten sadece a kullanmalısınız IList , ondan türetilmiş bir sınıf değil.

Sonuç / Düşünceler

Çok özel bir bağlamınız olduğunda, bir takımı oyuncular listesi olarak kabul etmek sorun olmaz. Örneğin, bir yöntemin içinde yazmak tamamen sorun değildir:

IList<Player> footballTeam = ...

F # kullanırken, bir tür kısaltması oluşturmak bile uygun olabilir:

type FootballTeam = IList<Player>

Ancak bağlam daha geniş veya net değilse, bunu yapmamalısınız. Bu, özellikle gelecekte kullanılabilecek bağlamı net olmayan yeni bir sınıf oluşturduğunuzda geçerlidir. Sınıfınıza ek özellikler (takımın adı, koçu vb.) Eklemeye başladığınızda bir uyarı işareti gelir. Bu, sınıfın kullanılacağı bağlamın sabit olmadığı ve gelecekte değişeceğinin açık bir işaretidir. Bu durumda takımı bir oyuncu listesi olarak düşünemezsiniz, ancak (şu anda aktif, yaralanmamış vb.) Oyuncuların listesini takımın bir özelliği olarak modellemelisiniz.


27

Sorunuzu yeniden yazayım. böylece konuyu farklı bir perspektiften görebilirsiniz.

Bir futbol takımını temsil etmem gerektiğinde, bunun temelde bir isim olduğunu anlıyorum. Gibi: "Kartallar"

string team = new string();

Sonra takımların da oyuncuları olduğunu fark ettim.

Neden dize türünü bir oyuncu listesi içerecek şekilde genişletemiyorum?

Soruna giriş noktanız keyfi. Bir takım yok düşünmek deneyin zorunda değil ne (özelliklerini) olduğunu .

Bunu yaptıktan sonra, özellikleri diğer sınıflarla paylaşıp paylaşmadığını görebilirsiniz. Ve kalıtım hakkında düşünün.


1
Bu iyi bir nokta - takım sadece bir isim olarak düşünülebilir. Ancak, eğer uygulamam oyuncuların eylemleriyle çalışmayı hedefliyorsa, bu düşünce biraz daha az açıktır. Her neyse, sorunun sonunda kompozisyon ile miras arasında olduğu anlaşılıyor.
Superbest

6
Bu, ona bakmak için çok değerli bir yoldur. Bir yan not olarak lütfen şunu düşünün: Bir zenginlik birkaç futbol takımına sahiptir, bir arkadaşına hediye olarak verir, arkadaş takımın adını değiştirir, koçu ateşler, ve tüm oyuncuların yerine geçer. Arkadaş ekibi yeşilde erkek takımıyla tanışır ve erkek takımı kaybederken "Sana verdiğim takımla beni dövdüğüne inanamıyorum!" adam doğru mu? bunu nasıl kontrol edersin?
user1852503

20

Diğer cevapların hemen hemen bir futbol takımının "a-a" List<FootballPlayer>veya "has-a" olup olmadığınıList<FootballPlayer> .

OP başlıca olarak aşağıdakilerden miras alınmasıyla ilgili kurallar hakkında açıklama ister List<T>:

Bir rehber miras kalmamanız gerektiğini söylüyor List<T>. Neden olmasın?

Çünkü List<T>sanal bir yöntemi yok. Bu, kendi kodunuzda daha az problemdir, çünkü uygulamayı nispeten az acıyla kapatabilirsiniz - ancak genel bir API'da çok daha büyük bir anlaşma olabilir.

Herkese açık bir API nedir ve neden ilgilenmeliyim?

Herkese açık bir API, üçüncü taraf programcılara gösterdiğiniz bir arayüzdür. Çerçeve kodunu düşünün. Ve atıfta bulunulan yönergelerin ".NET Uygulama Tasarımı Yönergeleri" değil, ".NET Framework Tasarım Yönergeleri" olduğunu unutmayın . Bir fark vardır ve - genel olarak konuşursak - genel API tasarımı çok daha katıdır.

Şu anki projemde bu herkese açık API bulunmuyorsa ve bu olasılığa sahip değilse, bu kılavuzu göz ardı edebilir miyim? Listeden miras alırsam ve genel bir API'ya ihtiyacım olduğu ortaya çıkarsa, ne tür zorluklar yaşayacağım?

Neredeyse, evet. Yine de durumunuz için geçerli olup olmadığını görmek için arkasındaki mantığı düşünmek isteyebilirsiniz, ancak genel bir API oluşturmuyorsanız, özellikle sürüm oluşturma gibi API endişeleri hakkında endişelenmenize gerek yoktur (ki bu bir alt kümesi).

Gelecekte herkese açık bir API eklerseniz, API'nızı uygulamanızdan soyutlamanız ( List<T>doğrudan göstermeyerek) veya yönergeleri, gelecekteki olası acıyla ihlal etmeniz gerekir .

Neden önemli? Liste bir listedir. Ne değişebilirdi? Neyi değiştirmek isteyebilirim?

Bağlama bağlıdır, ancak FootballTeamörnek olarak kullandığımızdan FootballPlayer, ekibin maaş sınırının üstesinden gelmesine neden olacaksa bir ekleyemeyeceğinizi hayal edin . Bunu eklemenin olası bir yolu şöyle olacaktır:

 class FootballTeam : List<FootballPlayer> {
     override void Add(FootballPlayer player) {
        if (this.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
        }
     }
 }

Ah ... ama yapamazsınız override Addçünkü virtual(performans nedenleriyle).

Bir uygulamadaysanız (temel olarak sizin ve tüm arayanlarınızın birlikte derlendiği anlamına gelir) artık IList<T>derleme hatalarını kullanmayı ve düzeltmeyi değiştirebilirsiniz :

 class FootballTeam : IList<FootballPlayer> {
     private List<FootballPlayer> Players { get; set; }

     override void Add(FootballPlayer player) {
        if (this.Players.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) {
          throw new InvalidOperationException("Would exceed salary cap!");
        }
     }
     /* boiler plate for rest of IList */
 }

ancak, genel olarak bir üçüncü tarafa maruz kaldıysanız derleme ve / veya çalışma zamanı hatalarına neden olacak bir değişiklik yaptınız.

TL; DR - kurallar herkese açık API'lar içindir. Özel API'lar için istediğinizi yapın.


18

İnsanların söylemesine izin veriyor mu?

myTeam.subList(3, 5);

hiç mantıklı mı? Değilse bir Liste olmamalıdır.


3
Eğer myTeam.subTeam(3, 5);
Sam Leach

3
@SamLeach Bunun doğru olduğu varsayılarak, hala miras değil kompozisyona ihtiyacınız olacak. Gibi artık subListbir daha döndürmez Team.
Cruncher

1
Bu beni diğer tüm cevaplardan daha fazla ikna etti. +1
FireCubez

16

Bu , "ekip" nesnenizin davranışına bağlıdır . Tıpkı bir koleksiyon gibi davranıyorsa, önce onu düz bir Liste ile temsil etmek uygun olabilir. Ardından, listede yinelenen kodu çoğaltmaya devam ettiğinizi fark etmeye başlayabilirsiniz; bu noktada oyuncu listesini saran bir FootballTeam nesnesi oluşturma seçeneğiniz vardır. FootballTeam sınıfı, oyuncular listesinde yinelenen tüm kodların evi olur.

Kodumu gereksiz yere ayrıntılı yapar. Şimdi sadece my_team.Count yerine my_team.Players.Count çağırmalıyım. Neyse ki, C # ile indekslemeyi şeffaf hale getirmek ve dahili Listenin tüm yöntemlerini iletmek için indeksleyicileri tanımlayabilirim ... Ama bu çok fazla kod! Tüm bu işler için ne alacağım?

Kapsülleme. Müşterilerinizin FootballTeam içinde neler olup bittiğini bilmeleri gerekmez. Tüm müşterilerinizin bildiği gibi, bir veritabanındaki oyuncuların listesine bakarak uygulanabilir. Bilmelerine gerek yok ve bu da tasarımınızı geliştiriyor.

Sadece sade bir anlam ifade etmiyor. Bir futbol takımının oyuncu listesi "yoktur". Oyuncuların listesidir. "John McFootballer SomeTeam oyuncularına katıldı" demiyorsunuz. "John SomeTeam'e katıldı" diyorsunuz. "Bir dizenin karakterlerine" bir harf eklemezsiniz, bir dizeye bir harf eklersiniz. Bir kitaplığın kitaplarına kitap eklemezsiniz, kitaplığa bir kitap eklersiniz.

Kesinlikle :) futbolTeam.Add (john) değil, footballTeam.List.Add (john) diyeceksiniz. Dahili liste görünmez.


1
OP, GERÇEK MODELİN nasıl yapılacağını anlamak istiyor ancak daha sonra bir futbol takımını futbolcuların listesi olarak tanımlıyor (kavramsal olarak yanlış). Sorun bu. Başka bir argüman bana göre yanlış yönlendiriyor.
Mauro Sampietro

3
Oyuncu listesinin kavramsal olarak yanlış olduğunu kabul etmiyorum. Şart değil. Birisi bu sayfada yazdığı gibi, "Tüm modeller yanlış, ama bazıları yararlı"
xpmatteo

Doğru modelleri elde etmek zordur, bir kez yapıldığında faydalı olanlardır. Bir model yanlış kullanılabilirse kavramsal olarak yanlıştır. stackoverflow.com/a/21706411/711061
Mauro Sampietro

15

Burada çok mükemmel cevaplar var, ancak bahsetmediğim bir şeye değinmek istiyorum: Nesneye yönelik tasarım, nesneleri güçlendirmekle ilgilidir .

Tüm kurallarınızı, ek çalışmalarınızı ve dahili detaylarınızı uygun bir nesnenin içine koymak istiyorsunuz. Bu şekilde, bununla etkileşime giren diğer nesnelerin hepsi için endişelenmesine gerek kalmaz. Aslında, bir adım daha ileri gitmek ve diğer nesnelerin bu iç kısımları atlamasını aktif olarak önlemek istersiniz .

Miras aldığınızda List , diğer tüm nesneler sizi Liste olarak görebilir. Oyuncu ekleme ve çıkarma yöntemlerine doğrudan erişimleri vardır. Ve kontrolünü kaybedeceksin; Örneğin:

Bir oyuncunun emekli olup olmadığını, istifa ettiğini veya işten atıldığını bilerek ayrılmak istediğinizi varsayalım. RemovePlayerUygun bir girdi enum'u alan bir yöntem uygulayabilirsiniz . Ancak, devralan tarafından List, doğrudan erişimi engellemek mümkün olacağını Remove, RemoveAllhatta ve Clear. Sonuç olarak, aslında ettik yetkilerini ellerinden senin FootballTeamsınıf.


Kapsülleme hakkında ek düşünceler ... Aşağıdaki endişeyi dile getirdiniz:

Kodumu gereksiz yere ayrıntılı yapar. Şimdi sadece my_team.Count yerine my_team.Players.Count çağırmalıyım.

Haklısın, bu, tüm müşterilerin seni takımını kullanması için gereksizce ayrıntılı olacaktır. Ancak, bu sorun maruz kaldığınız gerçeğe kıyasla çok küçükList Players ve muhtelif olmanızla böylece sizin izniniz olmadan ekibinizle kemanlanabilirler.

Söylemeye devam et:

Sadece düz bir anlam ifade etmiyor. Bir futbol takımının oyuncu listesi "yoktur". Oyuncuların listesidir. "John McFootballer SomeTeam oyuncularına katıldı" demiyorsunuz. "John SomeTeam'e katıldı" diyorsunuz.

İlk kısımda yanılıyorsunuz: 'liste' kelimesini bırakın ve aslında bir takımın oyuncuları olduğu açıktır.
Ancak, ikincisi ile kafasına çiviyi vurdunuz. İstemcilerin aramasını istemezsiniz ateam.Players.Add(...). Onların aramasını istiyorsun ateam.AddPlayer(...). Ve uygulamanız (muhtemelen diğer şeylerin yanı sıra) Players.Add(...)dahili olarak çağırırdı.


Umarım kapsüllemenin nesnelerinizi güçlendirmek amacıyla ne kadar önemli olduğunu görebilirsiniz. Her sınıfın, diğer nesnelerin karışmasından korkmadan işini iyi yapmasına izin vermek istiyorsunuz.


12

Bir veri yapısını temsil etmenin doğru C # yolu nedir ...

"Tüm modeller yanlış, ancak bazıları faydalı." - George EP Kutusu

"Doğru bir yol" yoktur, sadece yararlı bir yol vardır.

Sizin ve / kullanıcılarınız için yararlı olanı seçin. Bu kadar. Ekonomik olarak gelişin, fazla mühendislik yapmayın. Ne kadar az kod yazarsanız, o kadar az hata ayıklamanız gerekir. (aşağıdaki sürümleri okuyun).

- Düzenlendi

En iyi cevabım ... duruma göre değişir. Bir Listeden devralma, bu sınıftaki istemcileri, özellikle FootballTeam bir ticari varlık gibi göründüğü için, maruz bırakılmaması gereken yöntemlere maruz bırakacaktır.

- Baskı 2

“Aşırı mühendislik yapma” yorumunda neyi kastettiğimi içtenlikle hatırlamıyorum. KISS zihniyetinin iyi bir rehber olduğuna inanmama rağmen , bir iş sınıfını Listeden devralmanın, soyutlama sızıntısı nedeniyle çözdüğünden daha fazla sorun yaratacağını vurgulamak istiyorum. .

Öte yandan, Listeden miras almanın yararlı olduğu sınırlı sayıda vaka olduğuna inanıyorum. Önceki baskıda yazdığım gibi, duruma göre değişir. Her bir vakanın cevabı bilgi, deneyim ve kişisel tercihlerden büyük ölçüde etkilenir.

@Kai'ye cevap hakkında daha doğru düşünmeme yardımcı olduğu için teşekkürler.


En iyi cevabım ... duruma göre değişir. Bir Listeden devralma, bu sınıftaki istemcileri, özellikle FootballTeam bir ticari varlık gibi göründüğü için maruz bırakılmaması gereken yöntemlere maruz bırakacaktır. Bu cevabı düzenlemem mi yoksa başka bir mesaj mı yayınlamam gerektiğini bilmiyorum.
ArturoTena

2
" Bir Listsınıftan miras almak , bu sınıfın müşterilerini, maruz bırakılmaması gereken yöntemlere maruz bırakacaktır " konusundaki bit , kesin olarak Listmutlak hayır- hayır'dan miras kalan şeydir . Maruz kaldıktan sonra, zaman içinde yavaş yavaş istismar edilir ve yanlış kullanılırlar. Şimdi "ekonomik olarak gelişerek" zamandan tasarruf etmek, gelecekte kaybedilen tasarrufların on katına kolayca yol açabilir: kötüye kullanımların hata ayıklanması ve kalıtımın düzeltilmesi için yeniden düzenleme. YAGNI prensibi de anlam olarak düşünülebilir: Tüm bu yöntemlere ihtiyacınız olmayacak List, bu yüzden onları ifşa etmeyin.
Hayal kırıklığına uğramış

3
"fazla mühendislik yapmayın. Ne kadar az kod yazarsanız, hata ayıklamak için o kadar az kod gerekir." <- Bence bu yanıltıcı. Kapsülleme ve kalıtım üzerine kompozisyon fazla mühendislik DEĞİLDİR ve hata ayıklamaya daha fazla ihtiyaç duymaz. Kapsülleyerek, müşterilerin sınıfınızı kullanma (ve kötüye kullanma) yollarını SINIRLAYIRSINIZ ve böylece test ve giriş doğrulaması gerektiren daha az giriş noktasına sahip olursunuz. ListHızlı ve kolay olduğundan ve daha az hataya neden olacağı için miras almak yanlıştır, sadece kötü tasarım ve kötü tasarım "mühendislik" ten çok daha fazla hataya yol açar.
sara

@kai Her noktada sana katılıyorum. “Aşırı mühendislik yapma” yorumunda neyi kastettiğimi içtenlikle hatırlamıyorum. OTOH, sanırım Listeden miras almanın faydalı olduğu sınırlı sayıda vaka var. Daha sonraki baskıda yazdığım gibi, duruma göre değişir. Her bir vakanın cevabı bilgi, deneyim ve kişisel tercihlerden büyük ölçüde etkilenir. Hayattaki her şey gibi. ;-)
ArturoTena

11

Bu bana hatırlatıyor "karşı mı" karşı "ödünç var". Bazen doğrudan bir süper sınıftan miras almak daha kolaydır ve daha mantıklıdır. Diğer zamanlarda, bağımsız bir sınıf oluşturmak ve üye değişkeni olarak miras alacağınız sınıfı dahil etmek daha mantıklıdır. Sınıfın işlevselliğine yine de erişebilirsiniz, ancak arabirime veya sınıftan devralınmanın getirebileceği diğer kısıtlamalara bağlı değilsiniz.

Hangisini yapıyorsun? Birçok şeyde olduğu gibi ... bağlama bağlıdır. Kullanacağım rehber başka bir sınıftan miras almak için gerçekten bir "bir" ilişki olması gerektiğidir. BMW adında bir sınıf yazıyorsanız, BMW'den bir araba olduğu için Araba'dan miras alabilir. At sınıfı Memeli sınıfından miras alabilir, çünkü bir at gerçek hayatta bir memelidir ve herhangi bir Memeli işlevselliği At ile ilgili olmalıdır. Ama bir takımın bir liste olduğunu söyleyebilir misiniz? Söyleyebileceğim kadarıyla, bir Takım gerçekten "bir" Liste gibi görünmüyor. Yani bu durumda, üye değişkeni olarak bir List olurdu.


9

Kirli sırrım: İnsanların söylediklerini umursamıyorum ve yapıyorum. .NET Framework, "XxxxCollection" (UIElementCollection ile baş örneğimin üstü) ile yayıldı.

Öyleyse beni durduran şey:

team.Players.ByName("Nicolas")

Daha iyi bulduğumda

team.ByName("Nicolas")

Ayrıca, PlayerCollection'ım herhangi bir kod çoğaltması olmadan "Club" gibi başka bir sınıf tarafından kullanılabilir.

club.Players.ByName("Nicolas")

Dünün en iyi uygulamaları, yarınınki olmayabilir. En iyi uygulamaların arkasında bir neden yoktur, çoğu topluluk arasında sadece geniş bir anlaşmadır. Topluluğa bunu yaptığınızda sizi suçlayıp suçlamayacağını sormak yerine, daha okunabilir ve bakımı daha kolay olan nedir?

team.Players.ByName("Nicolas") 

veya

team.ByName("Nicolas")

Gerçekten mi. Hiç şüphen var mı? Şimdi belki <T> Listesini gerçek kullanım durumunuzda kullanmanızı engelleyen diğer teknik kısıtlamalarla oynamanız gerekebilir. Ancak olmaması gereken bir kısıtlama eklemeyin. Microsoft nedenini belgelemediyse, kesinlikle hiçbir yerden gelen bir "en iyi uygulama" dır.


9
Kabul edilen bilgeliğe uygun olduğunda meydan okuma cesaret ve inisiyatifine sahip olmak önemli olmakla birlikte, kabul edilen bilgeliğin, meydan okumaya başlamadan önce neden kabul edildiğini anlamanın akıllıca olduğunu düşünüyorum. -1 çünkü "Bu kılavuzu yoksay!" "Bu kılavuz neden var?" Bu arada, topluluk bu durumda sadece "beni suçlamak" değil, aynı zamanda beni ikna etmeyi başaran tatmin edici bir açıklama da sağlamıştır.
Superbest

3
Aslında, hiçbir açıklama yapılmadığında, tek yolun sınırı zorlamak ve testin ihlalden korkmamasını sağlamaktır. Şimdi. Liste <T> iyi bir fikir olmasa bile kendinize sorun. Kalıtımı <T> Listesinden <T> Koleksiyonuna değiştirmek ne kadar acı olur? Tahmin: Yeniden düzenleme araçlarıyla kodunuzun uzunluğu ne olursa olsun, forumda yayınlamaktan daha az zaman. Şimdi bu iyi bir soru, ama pratik bir soru değil.
Nicolas Dorier

Açıklığa kavuşturmak için: Kesinlikle SO'nun istediğimden miras almak için nimetini beklemiyorum, ama sorumun sorumu bu karara girmesi gereken hususları ve neden bu kadar deneyimli programcıya sahip olduğunu anlamaktı. yaptığımın tersine karar verdim. Collection<T>aynı değildir List<T>işin biraz geniş imsi projede doğrulamak isteyebilir buna yüzden.
Superbest

2
team.ByName("Nicolas")vasıtaları "Nicolas"ekibin adıdır.
Sam Leach

1
“olmaması gereken bir kısıtlama eklemeyin” Au contraire, ifşa etmek için iyi bir sebebiniz olmayan hiçbir şeyi ifşa etmeyin. Bu saflık veya kör zealotry değil, aynı zamanda en son tasarım moda trendi de değil. Temel nesne yönelimli tasarım 101, başlangıç ​​seviyesi. Bu "kural" ın neden var olduğu bir sır değil, onlarca yıllık deneyimin sonucudur.
sara

8

Yönergelerin söylediği, genel API'nın bir liste, bir küme, bir sözlük, bir ağaç veya başka bir şey kullanıp kullanmadığınıza ilişkin iç tasarım kararını açıklamamasıdır. "Takım" mutlaka bir liste değildir. Bunu bir liste olarak uygulayabilirsiniz, ancak genel API'nizin kullanıcıları sizi sınıfta bilmeleri gereken bir şekilde kullanmalıdır. Bu, genel arayüzü etkilemeden kararınızı değiştirmenize ve farklı bir veri yapısı kullanmanıza olanak tanır.


Geriye dönüp bakıldığında, @EricLippert ve diğerlerinin açıklamasından sonra, aslında sorumun API kısmı için harika bir cevap verdiniz - söylediklerinize ek olarak, eğer yaparsam class FootballTeam : List<FootballPlayers>, sınıfımın kullanıcıları bana şunu söyleyebilecekler ' miras ettik List<T>, yansıma kullanarak görerek List<T>bir futbol takımı için bir anlamı yok yöntemleri ve kullanabilme FootballTeamiçine List<T>ben bu yüzden, müşteriye açığa uygulama ayrıntıları olmak (gereksiz yere).
Superbest

7

List<T>"Optimize edilmiş" dedikleri zaman , biraz daha pahalı sanal yöntemler gibi özelliklere sahip olmadığı anlamına gelmek istediklerini düşünüyorum. Dolayısıyla sorun, herkese açık API'nızda ifşa List<T>ettiğinizde , iş kurallarını uygulama veya işlevselliğini daha sonra özelleştirme yeteneğinizi kaybetmenizdir. Ancak bu kalıtsal sınıfı projenizde dahili olarak kullanıyorsanız (binlerce müşterinize / ortaklarınıza / diğer ekiplerinize API olarak maruz kalmanın aksine) zaman kazandırır ve istediğiniz işlevselliktir çiftleme. API'larınızın ömrü boyunca miras almanın avantajı da iyi olabilir.List<T> öngörülebilir gelecekte asla özelleştirilmeyecek birçok aptal sarmalayıcı kodunu ortadan kaldırmanızdır. Ayrıca sınıfınızın açıkça aynı semantiğe sahip olmasını istiyorsanızList<T>

Çoğu insanın FxCop kuralı nedeniyle tonlarca ekstra iş yaptığını sık sık görüyorum ya da birinin blogu bunun "kötü" bir uygulama olduğunu söylüyor. Çoğu zaman, bu, desen palooza garipliğini tasarlamak için kodu açar. Çok sayıda kılavuzda olduğu gibi , istisnaları olabilen kılavuz olarak ele alın .


4

Bu cevapların çoğunun yaptığı gibi karmaşık bir karşılaştırmam olmasa da, bu durumu ele alma yöntemimi paylaşmak istiyorum. Genişleterek IEnumerable<T>, kendi izin verebilir Teamsınıf alenen tüm yöntem ve özellikleri göstermeden, Linq sorgusu uzantılarını destekleme List<T>.

class Team : IEnumerable<Player>
{
    private readonly List<Player> playerList;

    public Team()
    {
        playerList = new List<Player>();
    }

    public Enumerator GetEnumerator()
    {
        return playerList.GetEnumerator();
    }

    ...
}

class Player
{
    ...
}

3

Sınıf kullanıcılarınız tüm yöntemlere ve özelliklere ihtiyaç duyuyorsa ** Liste sahipse, sınıfınızı bundan türetmelisiniz. İhtiyaç duymuyorlarsa, listeyi ekleyin ve sınıf kullanıcılarınızın gerçekten ihtiyaç duyduğu yöntemler için sarmalayıcılar oluşturun.

Herkese açık bir API veya birçok kişi tarafından kullanılacak başka bir kod yazarsanız, bu katı bir kuraldır . Küçük bir uygulamanız ve en fazla 2 geliştiriciniz varsa bu kuralı göz ardı edebilirsiniz. Bu size biraz zaman kazandıracaktır.

Küçük uygulamalar için başka, daha az katı bir dil seçmeyi de düşünebilirsiniz. Ruby, JavaScript - daha az kod yazmanıza izin veren her şey.


3

Eiffel ve tasarımın sözleşmeli mucidi Bertrand Meyer'in bir göz kapağını vurmaktan çok Teammiras alacağını eklemek istedim List<Player>.

Nesneye Yönelik Yazılım İnşaatı kitabında, dikdörtgen pencerelerin alt pencerelere sahip olabileceği bir GUI sisteminin uygulanmasını tartışıyor. O sadece Windowikisinden miras alır Rectangleve Tree<Window>uygulamayı yeniden kullanır.

Ancak, C # Eiffel değil. İkincisi, çoklu kalıtım ve özelliklerin yeniden adlandırılmasını destekler . C # 'ta alt sınıfta, hem arabirimi hem de uygulamayı devralırsınız. Uygulamayı geçersiz kılabilirsiniz, ancak çağrı kuralları doğrudan üst sınıftan kopyalanır. Yeniden adlandırmak böylece Eiffel olarak, ancak, kamu yöntemlerin isimlerini değiştirebilir Addve Removehiç Hireve Fireöğenize Team. Eğer bir örneği Teamtekrar yayınlanırsa List<Player>, arayan onu değiştirecek Addve Removedeğiştirecek, ancak sanal yöntemleriniz Hireve Fireçağrılacaktır.


1
Burada sorun çoklu miras, mixins (vs ..) veya yokluğu ile nasıl başa çıkılacağı değildir. Herhangi bir dilde, insan dilinde bile bir takım bir insan listesi değil, bir insan listesinden oluşur. Bu soyut bir kavram. Bertrand Meyer veya herhangi bir takım alt sınıfla yönetiyorsa Liste yanlış yapıyor. Daha spesifik bir liste türü istiyorsanız, bir Listeyi alt sınıflamalısınız. Umarım katılıyorsun.
Mauro Sampietro

1
Bu size mirasın ne olduğuna bağlıdır. Tek başına, bu soyut bir işlemdir, ana akım yorum olsa bile, iki sınıfın "özel bir tür" ilişkide olduğu anlamına gelmez. Bununla birlikte, kompozisyon günümüzde tercih edilen alternatif olmasına rağmen, dil tasarımı bunu destekliyorsa , kalıtım uygulama yeniden kullanımı için bir mekanizma olarak ele alınabilir .
Alexey

2

Bence genellemenize katılmıyorum. Bir takım sadece bir oyuncu topluluğu değildir. Bir takımın adı, amblemi, yönetim / yönetici personelinin toplanması, koçluk ekibinin toplanması, daha sonra oyuncuların toplanması hakkında çok daha fazla bilgisi vardır. Dolayısıyla, FootballTeam sınıfınızda 3 koleksiyon olmalı ve kendisi bir koleksiyon olmamalıdır; gerçek dünyayı düzgün bir şekilde modellemek için.

Specialized StringCollection gibi, iç depoya nesneler eklenmeden veya iç depodan kaldırılmadan önce doğrulama ve kontroller gibi başka özellikler sunan bir PlayerCollection sınıfını düşünebilirsiniz.

Belki de bir PlayerCollection bahisleri fikri tercih ettiğiniz yaklaşıma uyuyor?

public class PlayerCollection : Collection<Player> 
{ 
}

Ve sonra FootballTeam şöyle görünebilir:

public class FootballTeam 
{ 
    public string Name { get; set; }
    public string Location { get; set; }

    public ManagementCollection Management { get; protected set; } = new ManagementCollection();

    public CoachingCollection CoachingCrew { get; protected set; } = new CoachingCollection();

    public PlayerCollection Players { get; protected set; } = new PlayerCollection();
}

2

Sınıflara Göre Arabirimleri Tercih Etme

Sınıflar sınıflardan türetmekten kaçınmalı ve bunun yerine gerekli asgari arayüzleri uygulamalıdır.

Kalıtım Kapsülleme tatili

Sınıflardan türetme kapsüllemeyi keser :

  • koleksiyonunuzun nasıl uygulandığına ilişkin dahili ayrıntıları gösterir
  • uygun olmayan bir arabirimi (genel işlevler ve özellikler kümesi) bildirir

Diğer şeylerin yanı sıra bu, kodunuzu yeniden düzenlemeyi zorlaştırır.

Sınıflar Uygulama Detayıdır

Sınıflar, kodunuzun diğer bölümlerinden gizlenmesi gereken bir uygulama ayrıntısıdır. Kısacası a System.List, şimdi ve gelecekte uygun olabilecek veya olmayabilecek soyut bir veri tipinin özel bir uygulamasıdır.

Kavramsal olarak System.Listveri türünün "liste" olarak adlandırılması, biraz kırmızı ringa balığıdır. A System.List<T>, öğelerin eklenmesi, eklenmesi ve çıkarılması için itfa edilmiş O (1) işlemlerini ve öğelerin sayısını almak veya öğeyi dizine göre almak ve ayarlamak için O (1) işlemlerini destekleyen değiştirilebilir bir koleksiyondur.

Arayüz Ne kadar Küçükse Kod Daha Esnek

Bir veri yapısı tasarlanırken arayüz ne kadar basit olursa, kod o kadar esnek olur. Bunun bir gösterimi için LINQ'nun ne kadar güçlü olduğuna bakın.

Arayüzler Nasıl Seçilir

"Liste" diye düşündüğünüzde kendinize, "Beyzbol oyuncularının bir koleksiyonunu temsil etmeliyim" diyerek başlamalısınız. Diyelim ki bunu bir sınıfla modellemeye karar verdiniz. İlk önce yapmanız gereken şey, bu sınıfın ne kadar az arayüz açığa çıkarması gerektiğine karar vermektir.

Bu sürece rehberlik edebilecek bazı sorular:

  • Sayıma ihtiyacım var mı? Uygulamayı düşünmüyorsanızIEnumerable<T>
  • Bu koleksiyon başlatıldıktan sonra değişecek mi? Değilse düşünün IReadonlyList<T>.
  • Öğelere dizine göre erişebilmem önemli mi? DüşünmekICollection<T>
  • Koleksiyona ürün eklediğim sipariş önemli mi? Belki bir ISet<T>?
  • Eğer gerçekten bu şeyi istiyorsanız o zaman devam edin ve uygulayın IList<T>.

Bu şekilde, kodun diğer kısımlarını beyzbol oyuncuları koleksiyonunuzun uygulama ayrıntılarına bağlamayacaksınız ve arayüze saygı duyduğunuz sürece uygulanma şeklini değiştirmekte özgür olacaksınız.

Bu yaklaşımı kullanarak, kodun okunması, yeniden düzenlenmesi ve tekrar kullanılması kolaylaşır.

Isıtıcıdan Kaçınma Hakkında Notlar

Modern bir IDE'de arayüzlerin uygulanması kolay olmalıdır. Sağ tıklayın ve "Arabirimi Uygula" yı seçin. Ardından, gerekiyorsa tüm uygulamaları bir üye sınıfa iletin.

Bununla birlikte, çok sayıda kazan plakası yazdığınızı fark ederseniz, bunun nedeni, olması gerekenden daha fazla işlev ortaya çıkarmanızdır. Bir sınıftan miras almamanız da aynı nedendir.

Ayrıca uygulamanız için anlamlı olan daha küçük arayüzler ve belki de bu arayüzleri ihtiyacınız olan diğerleriyle eşleştirmek için sadece birkaç yardımcı uzantı işlevi tasarlayabilirsiniz. LinqArray kütüphanesiIArray için kendi arayüzümde kullandığım yaklaşım bu .


2

Serileştirme ile ilgili sorunlar

Bir yönü eksik. List <> öğesinden devralan sınıflar, XmlSerializer kullanılarak doğru şekilde serileştirilemez . Bu durumda bunun yerine DataContractSerializer kullanılmalıdır veya kendi serileştirme uygulaması gerekir.

public class DemoList : List<Demo>
{
    // using XmlSerializer this properties won't be seralized:
    string AnyPropertyInDerivedFromList { get; set; }     
}

public class Demo
{
    // this properties will be seralized
    string AnyPropetyInDemo { get; set; }  
}

İlave okuma: Bir sınıf <> Listesinden devralındığında, XmlSerializer diğer nitelikleri serileştirmez


0

Ne zaman kabul edilebilir?

Eric Lippert'i alıntılamak için:

Mekanizmayı uzatan bir mekanizma inşa ederken .List<T>

Örneğin, AddRangeyöntemin yokluğunda bıktınız IList<T>:

public interface IMoreConvenientListInterface<T> : IList<T>
{
    void AddRange(IEnumerable<T> collection);
}

public class MoreConvenientList<T> : List<T>, IMoreConvenientListInterface<T> { }
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.