Yapılar ve sınıflar neden C # kavramlarını ayrı tutarlar?


44

C # ile programlama yaparken tutamayacağım tuhaf bir dil tasarım kararına rastladım.

Bu nedenle, C # (ve CLR) iki toplu veri türüne sahiptir: struct(yığın üzerinde depolanan değer türü, kalıtım yok) ve class(öbek üzerinde depolanan referans türü, kalıtımsal).

Bu kurulum ilk önce kulağa hoş geliyor, ancak daha sonra bir toplama türünün parametresini alan bir yönteme rastlarsınız ve gerçekte bir değer türünde mi yoksa referans türünde mi olduğunu bulmak için türünün bildirimini bulmanız gerekir. Bazen çok kafa karıştırıcı olabilir.

Soruna genel kabul görmüş çözüm, olası hataları önlemek ve faydalarını sınırlamak için hepsinin struct"değişmez" (tarlalarını belirleyerek readonly) olarak ilan ettiği görülüyor struct.

C ++, örneğin, çok daha kullanışlı bir model kullanıyor: Bir yığın örneği veya yığın üzerinde bir nesne örneği oluşturmanıza ve değeri veya referans (veya işaretçi) ile geçirmenize izin veriyor. C # 'nin C ++' dan ilham aldığını duymaya devam ediyorum ve neden bu tek bir tekniği üstlenmediğini anlamıyorum. Birleştiren classve structüzeri referans olarak (açıkça) değerleri olarak onları etrafında geçen ya da iki farklı tahsisat seçenekleri (yığın ve) ve bir yapı içine refve outanahtar kelimeler güzel şey gibi görünüyor.

Soru, neden vermedi edilir classve structayrı C # kavram ve yerine iki tahsisat seçenekleri ile bir agrega Çeşidi CLR haline?


34
“Soruna genel kabul görmüş çözüm, tüm yapıların“ değişmez ”olarak nitelendirildiğini gösteriyor ... yapıların faydasını sınırlandırıyor gibi görünüyor” Pek çok kişi, değişmez bir şey yapmanın genellikle bir performans darboğazının nedeni olmadığı durumlarda daha kullanışlı hale getirdiğini iddia eder . Ayrıca, structher zaman yığında saklanmaz; structalanı olan bir nesneyi düşünün . Bu bir yana, Mason Wheeler'ın belirttiği gibi, dilimleme problemi muhtemelen en büyük sebep.
Doval

7
C # 'ın C ++' dan ilham aldığı doğru değil; bunun yerine C #, hem C ++ hem de Java tasarımındaki (o sırada iyi niyetli ve iyi ses veren) hatalardan ilham almıştır.
Pieter Geerkens

18
Not: yığın ve yığın uygulama detaylarıdır. Yapısal örneklerin yığına tahsis edilmesi gerektiğini ve sınıf örneklerinin öbek üzerine tahsis edilmesi gerektiğini söyleyen hiçbir şey yoktur. Ve aslında doğru bile değil. Örneğin, bir derleyicinin Escape Analizini kullanarak bir değişkenin yerel kapsamdan çıkamayacağını ve dolayısıyla bir sınıf örneği olsa bile yığına tahsis edemeyeceğini belirlemesi çok iyi bir olasılıktır. Bir yığın ya da bir yığın olması gerektiğini bile söylemiyor. Ortam çerçevelerini öbek üzerinde bağlantılı bir liste olarak tahsis edebilir ve hatta bir yığının bile olmamasını sağlayabilirsiniz.
Jörg W Mittag


3
"ancak daha sonra toplam türün bir parametresini alan bir yönteme rastlarsınız ve gerçekte bir değer türünde mi yoksa bir referans türünde mi olduğunu anlamak için türünün bildirimini bulmanız gerekir" Umm, neden bu önemli? ? Bu yöntem sizden bir değer iletmenizi ister. Bir değer iletin. Hangi noktada bir referans türü veya bir değer türü olduğu ile ilgilenmelisiniz?
Luaan

Yanıtlar:


58

Bunun nedeni C # (ve Java ve C ++ 'dan sonra geliştirilen tüm diğer OO dilleri), C ++' nın modelini bu açıdan kopyalamamış olmasıdır, çünkü C ++ 'ın yaptığı şey korkunç bir karışıklıktır.

Yukarıdaki ilgili noktaları doğru bir şekilde tanımladınız struct: değer türü, miras yok. class: başvuru türü, miras var. Kalıtım ve değer türleri (veya daha spesifik olarak, polimorfizm ve parola değeri) karışmaz; bir tür nesnesini, türün Derivedbir argümanına iletip Basesanal bir yöntem çağırırsanız, uygun davranışı elde etmenin tek yolu, geçen şeyin referans olduğunu sağlamaktır.

Bu ve C ++ 'da karşılaştığınız diğer tüm karışıklıklar arasında, değer türleri olarak kalıtsal nesneler (kopya kurucular ve nesne dilimleme akla geliyor!) En iyi çözüm Just Say No'dur.

İyi dil tasarımı sadece özellikleri uygulamak değildir, aynı zamanda hangi özelliklerin uygulanmamasını da bilir ve bunu yapmanın en iyi yollarından biri sizden önce gelenlerin hatalarından ders almaktır.


29
Bu C ++ 'da henüz anlamsız başka bir öznel rant. Aşağı oy kullanamazsın, ama yapabilirsem olur.
Bartek Banachewicz

17
@MasonWheeler: " korkunç bir karmaşa " kulağa yeterince öznel geliyor. Bu, uzun bir yorum başlığında sizinkinin başka bir cevabına değindi ; iş parçacığı tükendi (ne yazık ki, çünkü alev savaş sosunda da faydalı yorumlar içeriyordu). Buradaki her şeyi tekrarlamaya değeceğini sanmıyorum, ancak "C # doğru ve C ++ yanlış anlamıştı" (iletmeye çalıştığınız mesaj gibi görünüyor) gerçekten öznel bir ifadedir.
Andy Prowl

15
@MasonWheeler: Nükleer iş parçacığında çalıştım ve birkaç başka insan da yaptım - bu yüzden silinmesinin talihsiz olduğunu düşünüyorum. Bu konuyu burada çoğaltmanın iyi bir fikir olduğunu sanmıyorum, ancak kısa versiyon şudur: C ++ 'da , türün kullanıcısı , tasarımcının değil, bir türün hangi anlambilimiyle kullanılması gerektiğine karar verir (referans semantiği veya değer semantik). Bunun avantaj ve dezavantajları vardır: Artıları düşünmeden (veya bilmeden) eksilere karşı öfkeleniyorsunuz. Bu yüzden analiz özneldir.
Andy Prowl

7
nefes Ben LSP ihlali tartışması şimdiden yerini almıştır inanıyoruz. Ve ben tür insanların çoğunluğu LSP söz oldukça garip ve ilgisiz olmakla çünkü kontrol edemez kabul inanıyoruz bir mod yorum dizisi nuke .
Bartek Banachewicz

9
Son paragrafınızı en üste taşır ve geçerli ilk paragrafı silerseniz, mükemmel bir tartışmaya sahip olduğunuzu düşünüyorum. Ancak mevcut ilk paragraf sadece özneldir.
Martin York

19

Benzer bir şekilde, C # temel olarak birisinin genellikle pense ve ayarlanabilir anahtarlardan kaçınmanız gerektiğini okuduğu bir dizi mekanik araca benzer, bu nedenle hiçbir zaman ayarlanabilir anahtarlar içermez ve pense "güvensiz" olarak işaretlenmiş özel bir çekmeceye kilitlenir ve işvereninize sağlığınızla ilgili herhangi bir sorumluluktan vazgeçen bir feragatnameyi imzaladıktan sonra ancak bir süpervizörün onayı ile kullanılabilir.

C ++, kıyaslandığında, yalnızca ayarlanabilir anahtarlar ve penseler değil, aynı zamanda amacı hemen belli olmayan garip toplu özel amaçlı araçlar içermez ve onları tutmanın doğru yolunu bilmiyorsanız, kolayca kesebilirler. thumb (ancak onları nasıl kullanacağınızı anladıktan sonra, C # araç kutusundaki temel araçlarla temel olarak imkansız olan şeyleri yapabilirsiniz). Ek olarak, ihtiyaç duyduğunuz zaman tamamen yeni aletler tasarlamanıza ve yaratmanıza olanak tanıyan bir torna, freze tezgahı, yüzey taşlama makinesi, metal kesme şerit testere, vb. Vardır (ancak, bu makinistlerin aletleri neden olabilir ve Onlarla ne yaptığınızı bilmiyorsanız (veya sadece dikkatsiz olsanız bile) ciddi yaralanmalar.

Bu, felsefedeki temel farkı yansıtır: C ++, temel olarak isteyebileceğiniz herhangi bir tasarım için ihtiyaç duyabileceğiniz tüm araçları size vermeye çalışır. Bu araçları nasıl kullandığınızı kontrol etmeyi neredeyse hiç denemez, bu nedenle, yalnızca nadir durumlarda iyi işleyen tasarımların yanı sıra muhtemelen sadece berbat bir fikir olan ve hiç kimsenin durumdan haberi olmayan tasarımlar üretmek için de kullanımı kolaydır. iyi çalışacaklar. Özellikle, bunun büyük bir kısmı tasarım kararlarının ayrıştırılmasıyla yapılır - pratikte neredeyse her zaman birbirine bağlı olanlar bile. Sonuç olarak, sadece C ++ yazmakla C ++ yazmak arasında çok büyük bir fark var. C ++ 'ı iyi yazmak için, bir çok deyim ve kurallara dikkat edin (kurallara uymadan önce kuralların ne kadar ciddiye alınacağına dair kurallar dahil). Sonuç olarak, C ++, öğrenme kolaylığına (uzmanlar tarafından) kullanım kolaylığına çok daha fazla yönelmiştir. Ayrıca kullanımının gerçekten de çok kolay olmadığı durumlar da var.

C # (veya en azından kuvvete denemek için çok daha fazla yapar son derece güçlü önermek) dil tasarımcıları iyi tasarım uygulamalarını ne kabul. C ++ 'da ayrılan (ancak genellikle pratikte bir arada olan) bir kaç şey doğrudan C #' da bir araya getirilir. "Güvensiz" kodun sınırları biraz zorlamasına, ancak dürüst olmak gerekirse, çok fazla olmasına izin vermez.

Sonuç olarak, bir yandan doğrudan C ++ 'da ifade etmek için oldukça beceriksiz olan doğrudan C ++ ile ifade edilebilen birkaç tasarım vardır. Öte yandan, bu bir var bütün C # öğrenmek için çok daha kolay ve durumunuza değil iş (veya muhtemelen başka) olan bir gerçekten korkunç bir tasarım üretme şansı büyük ölçüde azalttı. Çoğu (muhtemelen çoğu durumda) durumlarda, tabiri caizse "akışa devam ederek" sağlam ve uygulanabilir bir tasarım elde edebilirsiniz. Ya da arkadaşlarımdan biri olarak (en azından onu arkadaş olarak düşünmeyi seviyorum - gerçekten de kabul ederse emin değil) koymaktan hoşlanıyor, C # başarının çukuruna düşmeyi kolaylaştırıyor.

Bu nedenle , iki dilde nasıl oldukları classve structnasıl oldukları sorusuna daha özel bir şekilde bakalım: türetilmiş bir sınıfın nesnesini temel sınıf / arabirim kılığında kullanabileceğiniz bir kalıtım hiyerarşisinde yaratılan nesneler, oldukça normalde bir çeşit işaretçi veya referans yoluyla yapmanız gerekmesi gerçeğiyle sıkışmış durumda - somut bir seviyede, türetilmiş sınıfın nesnesinin temel sınıfın bir örneği olarak ele alınabilecek bir şey içerdiği / arayüz ve türetilmiş nesne, hafızanın o kısmının adresi aracılığıyla manipüle edilir.

C ++ 'da bunu doğru yapmak programcının görevidir - kalıtım kullanırken, bir hiyerarşideki polimorfik sınıflarla çalışan bir işlevin bunu bir işaretçi veya taban referansı ile yapmasını sağlamak ona bağlıdır. sınıf.

C # 'da, temelde türler arasındaki aynı ayrımın ne olduğu daha açıktır ve dilin kendisi tarafından uygulanır. Programcının bir sınıfın örneğini referans olarak geçmek için herhangi bir adım atması gerekmez, çünkü bu varsayılan olarak gerçekleşir.


2
C ++ hayranı olarak, bunun C # ve İsviçre Ordusu Testere testeresi arasındaki farkların mükemmel bir özeti olduğunu düşünüyorum.
David Thornley

1
@DavidThornley: En azından biraz dengeli bir karşılaştırma olacağını düşündüğüm şeyi yazmaya çalıştım. Parmakları işaret etmek değil, ama bunu yazarken gördüğüm bir kısmı beni ... biraz yanlış olarak çarptı (güzelce söylemek gerekirse).
Jerry Coffin,

7

Bu "C #: Neden Başka Bir Dile İhtiyacımız Var?" - Gunnerson, Eric:

Sadelik C # için önemli bir tasarım hedefi oldu.

Sadelik ve dil saflığı konusunda denize girmek mümkündür, ancak saflık uğruna saflık, profesyonel programcı için pek kullanılmaz. Bu nedenle, programcıların karşılaştığı gerçek dünyadaki problemleri çözerek basit ve özlü bir dile sahip olma isteğimizi dengelemeye çalıştık.

[...]

Değer türleri , operatörün aşırı yüklenmesi ve kullanıcı tanımlı dönüşümlerin hepsi dile karmaşıklık katar , ancak önemli bir kullanıcı senaryosunun inanılmaz derecede basitleştirilmesine izin verir.

Nesneler için referans anlambilimi, birçok sıkıntıdan kaçınmanın bir yoludur (tabii ki sadece nesne dilimlemeyi değil) ama gerçek dünya problemleri bazen değer anlamında olan nesneler gerektirebilir (örn. Asla referans anlambilim kullanmamalıyım gibi göz atın , değil mi? farklı bir bakış açısı için).

Bu nedenle, etiketi altında anlamsal olan kirli, çirkin ve kötü nesneleri ayırmaktan daha iyi bir yaklaşım ne olabilir struct?


1
Bilmiyorum, belki de bu kirli, çirkin ve kötü niyetli referans-anlambilimsel nesnelerini kullanmıyor musun?
Bartek Banachewicz

Belki ... Ben kaybedilen bir nedenim.
manlio

2
Java tasarımındaki en büyük hatalardan biri olan IMHO, bir değişkenin kimliği mi yoksa mülkiyeti mi içermek için kullanıldığını bildirmek için herhangi bir araç bulunmaması ve C # 'daki en büyük hatalardan biri, işlemleri ayırt etmek için bir araç olmamasıdır. bir değişkenin referans aldığı bir nesne üzerindeki işlemlerden bir değişken. Çalışma Zamanı bu tür farklılıkları umursamasa bile, bir dilde bir tür değişkenin int[]paylaşılabilir veya değiştirilebilir (diziler olabilir, ancak genel olarak her ikisi de olmayabilir) olup olmadığını belirtmek yanlış kodun yanlış görünmesine yardımcı olur.
supercat

4

ObjectTamamen türetilen değer türlerini düşünmekten ziyade , tamamen ayrı bir evrende var olan depo yeri tiplerini sınıf örneği türlerinden düşünmek, ancak her değer türünün karşılık gelen bir yığın-nesne türüne sahip olduğunu düşünmek daha yararlı olacaktır. Yapı türünün depolandığı yer, yalnızca türün genel ve özel alanlarının birleşimini tutar ve öbek türü, aşağıdaki gibi bir desene göre otomatik olarak oluşturulur:

// Defined structure
struct Point : IEquatable<Point>
{
  public int X,Y;
  public Point(int x, int y) { X=x; Y=y; }
  public bool Equals(Point other) { return X==other.X && y==other.Y; }
  public bool Equals(Object other)
  { return other != null && other.GetType()==typeof(this) && Equals(Point(other)); }
  public bool ToString() { return String.Format("[{0},{1}", x, y); }
  public bool GetHashCode() { return unchecked(x+y*65531); }
}        
// Auto-generated class
class boxed_Point: IEquatable<Point>
{
  public Point value; // Fake name; C++/CLI, though not C#, allow full access
  public boxed_Point(Point v) { value=v; }
  // Members chain to each member of the original
  public bool Equals(Point other) { return value.Equals(other); }
  public bool Equals(Object other) { return value.Equals(other); }
  public String ToString() { return value.ToString(); }
  public Int32 GetHashCode() { return value.GetHashCode(); }
}

ve şöyle bir ifade için: Console.WriteLine ("Değer {0}", somePoint);

olarak çevrilecek: boxed_Point box1 = new boxed_Point (somePoint); Console.WriteLine ("Değer {0}", kutu1);

Uygulamada, depolama konumu türleri ve yığın örneği türleri ayrı evrenlerde bulunduğundan, yığın örneği türlerini şöyle boxed_Int32; Çünkü sistem hangi içeriklerin yığın-nesne örneğini gerektirdiğini ve hangilerinin bir depolama yeri gerektirdiğini bilecektir.

Bazı insanlar, nesneler gibi davranmayan herhangi bir değer türünün "kötü" olarak kabul edilmesi gerektiğini düşünmektedir. Tersine görüyorum: Değer türlerinin saklama yerleri ne nesne ne de nesnelere referans olmadığından, nesneler gibi davranmaları gerektiği yararsızdır. Bir yapının, bir nesne gibi faydalı bir şekilde davranabildiği durumlarda, bunu yapmanın yanlış bir tarafı yoktur, ancak her birinin structkalbi, koli bandı ile birbirine yapışmış halka açık ve özel alanların toplanmasından başka bir şey değildir.

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.