“Set” bir Get metoduna sahip olmalı mı?


22

Hadi bu C # sınıfına sahip olalım (Java'da neredeyse aynı olurdu)

public class MyClass {
   public string A {get; set;}
   public string B {get; set;}

   public override bool Equals(object obj) {
        var item = obj as MyClass;

        if (item == null || this.A == null || item.A == null)
        {
            return false;
        }
        return this.A.equals(item.A);
   }

   public override int GetHashCode() {
        return A != null ? A.GetHashCode() : 0;
   }
}

Görebildiğiniz gibi, iki vakanın eşitliği yalnızca MyClassbağlıdır A. Dolayısıyla eşit olan ancak Bmülklerinde farklı bilgiler taşıyan iki örnek olabilir .

Pek çok dilin (tabii ki C # ve Java dahil) standart bir koleksiyon kütüphanesinde, her bir eşit örnek kümesinden en fazla bir öğeyi tutabilen bir koleksiyonun Set( HashSetC # içinde) olduğu görülmektedir.

Bir öğe ekleyebilir, öğeleri kaldırabilir ve setin bir öğe içerip içermediğini kontrol edebilirsiniz. Ancak belirli bir öğeyi setten almak neden imkansız?

HashSet<MyClass> mset = new HashSet<MyClass>();
mset.Add(new MyClass {A = "Hello", B = "Bye"});

//I can do this
if (mset.Contains(new MyClass {A = "Hello", B = "See you"})) {
    //something
}

//But I cannot do this, because Get does not exist!!!
MyClass item = mset.Get(new MyClass {A = "Hello", B = "See you"});
Console.WriteLine(item.B); //should print Bye

Maddemi almanın tek yolu, tüm koleksiyon boyunca yinelemektir ve tüm öğeleri eşitlik açısından kontrol etmektir. Ancak, O(n)bunun yerine zaman alır O(1)!

Şimdiye kadarki bir setten almayı destekleyen bir dil bulamadım. Tanıdığım tüm "ortak" diller (Java, C #, Python, Scala, Haskell ...) aynı şekilde tasarlanmış gibi görünüyor: öğeler ekleyebilirsiniz, ancak bunları alamazsınız. Tüm bu dillerin kolay ve bariz bir şekilde faydalı bir şeyi desteklememesinin iyi bir nedeni var mı? Hepsi yanlış olamaz, değil mi? Bunu destekleyen diller var mı? Belki belirli bir öğeyi setten tekrar almak yanlıştır, fakat neden?


SO ile ilgili birkaç soru var:

/programming/7283338/getting-an-element-from-a-set

/programming/7760364/how-to-retrieve-actual-item-from-hashsett


12
C ++ std::setnesne alımını destekler, yani "ortak" dillerin hepsi tanımladığınız gibi değildir.
Monica

17
"İki MyClass örneğinin eşitliği yalnızca A'ya bağlı" olduğunu iddia ederseniz (ve kodlarsanız), aynı A değerine ve farklı B'ye sahip olan bir başka örnek etkin olarak "bu özel örnek" olur, çünkü sizin eşit olduklarını B'deki farklılıklar önemli değil; kabın, eşit olduğundan diğer örneği geri getirmesine "izin verilir".
Peteris

7
Gerçek hikaye: Java'da birçok Set<E>uygulama yalnızca Map<E,Boolean>içeridedir.
corsiKa

10
A kişiyle konuşmak : "Merhaba, lütfen A Kişi'ni buraya getirebilir misiniz?"
Brad Thomas

7
Bu a == bdurumda refleksivite ( her zaman doğru) kırılır this.A == null. if (item == null || this.A == null || item.A == null)Test muhtemelen yapay "kaliteli" kod yaratmak için, çok için "abartılı" ve çekler olduğunu. Bu tür bir "overchecking" ve Kod İncelemesinde her zaman aşırı dürüst olduğumu görüyorum.
usr

Yanıtlar:


66

Buradaki sorun HashSetbir Getyöntemden yoksun değil, kodunuzun HashSettürün bakış açısından bir anlamı yok .

Bu Getyöntem, .NET Framework halkının mantıklı bir şekilde yanıtlayacağı "bana bu değeri lütfen al" dır <confused face />.

Öğeleri saklamak ve daha sonra biraz farklı bir değere eşleştirmek temelinde bunları almak Dictionary<String, MyClass>istiyorsanız , sonra kullanabildiğiniz kadar kullanın :

var mset = new Dictionary<String, MyClass>();
mset.Add("Hello", new MyClass {A = "Hello", B = "Bye"});

var item = mset["Hello"];
Console.WriteLine(item.B); // will print Bye

Eşitlik bilgisi kapsüllenmiş sınıftan sızıyor. Dahil olan özellik kümesini değiştirmek isteseydim Equals, kodu dışardan değiştirmek zorunda kalırdım MyClass...

Evet, ama bunun nedeni en MyClassaz şaşkınlık (POLA) prensibiyle uyuşması. Bu eşitlik işlevi kapsüllenmiş durumdayken, aşağıdaki kodun geçerli olduğunu varsaymak tamamen mantıklıdır:

HashSet<MyClass> mset = new HashSet<MyClass>();
mset.Add(new MyClass {A = "Hello", B = "Bye"});

if (mset.Contains(new MyClass {A = "Hello", B = "See you"})) 
{
    // this code is unreachable.
}

Bunu önlemek MyClassiçin, tuhaf eşitlik biçimine dair açıkça belgelenmesi gerekir. Bunu yaptıktan sonra, artık kapsüllenmiş değildir ve bu eşitlik çalışmasının açık / kapalı prensibini nasıl bozacağını değiştirmek artık mümkün değildir. Ergo, değişmemeliydi ve bu nedenle Dictionary<String, MyClass>bu garip ihtiyaç için iyi bir çözüm.


2
@vojta, Bu durumda, kullandığı Dictionary<MyClass, MyClass>anahtara göre değeri getireceği gibi kullanın MyClass.Equals.
David Arno

8
Bir Dictionary<MyClass, MyClass>tedarikçiyi uygun bir şekilde kullanırdım IEqualityComparer<MyClass>ve denklik ilişkisini , örnekleri arasındaki MyClassneden MyClassbu ilişki hakkında bilmek zorunda olduğunu çıkarırdım?
Caleth

16
@vojta ve buradaki yorum: " meh. Eşit olmayan nesnelerin" eşit "olması için eşitlerin uygulanmasını geçersiz kılmak burada sorun." Bana bu nesnenin özdeş nesnesini getir "yazan bir yöntem sormak ve sonra özdeş olmayan bir nesnenin iade edilmesini beklemek deli gibi görünüyor ve bakım sorunlarına neden olması kolay " gibi görünüyor . SO'daki sık sık sorun budur: ciddi şekilde hatalı cevaplar, bozuk kodlarına hızlı bir şekilde ulaşma isteklerini yerine getirme isteklerini ima etmemiş olan halk tarafından tahmin edilemez ...
David Arno

6
@DavidArno: Eşitlik ve kimliği birbirinden ayıran dilleri kullanmaya devam ettiğimiz sürece, kaçınılmazdır ;-) Eşit fakat özdeş olmayan nesneleri kanonlaştırmak istiyorsanız, o zaman bana "özdeş değil" yazan bir yönteme ihtiyacınız var. bu nesneye itiraz et ", ancak" bana bu nesneye eşit olan kanonik nesneyi getir ". Bu dillerde HashSet.Get'in mutlaka "aynı nesneyi bana getir" anlamına geldiğini düşünen herkes çoktan hatalıdır.
Steve Jessop

4
Bu cevap gibi birçok battaniyeye sahiptir ...reasonable to assume.... Tüm bunlar vakaların% 99'unda doğru olabilir, ancak yine de bir kümeden bir eşya alma yeteneği kullanışlı olabilir. Gerçek dünya kodu her zaman POLA vb. İlkelere bağlı kalamaz. Örneğin, büyük / küçük harf duyarsızca dizeleri tekilleştiriyorsanız, "ana" öğeyi almak isteyebilirsiniz. Dictionary<string, string>geçici bir çözümdür, ancak maliyeti yüksektir.
usr

24

Sette "in" olan bir öğeye zaten sahipsiniz - anahtarı olarak geçtiniz.

“Ama bu, Add ile adlandırdığım örnek değil” - Evet, ama özellikle eşit olduklarını iddia ettiniz.

A Setda özel bir durum Map| Dictionary, değer türü olarak geçersiz (iyi işe yaramaz yöntemler tanımlanmamıştır, ancak bu önemli değildir).

Aradığınız veri yapısı bir olduğu Dictionary<X, MyClass>yerde Xher nasılsa MyClasses dışına Farklı alır.

C # Sözlük tipi, anahtarlar için bir IEqualityComparer sağlamanıza izin verdiği için bu konuda güzeldir.

Verilen örnek için aşağıdakilere sahip olurdum:

public class MyClass {
   public string A {get; set;}
   public string B {get; set;}
}

public class MyClassEquivalentAs : IEqualityComparer<MyClass>{
   public override bool Equals(MyClass left, MyClass right) {
        if (Object.ReferenceEquals(left, null) && Object.ReferenceEquals(right, null))
        {
            return true;
        }
        else if (Object.ReferenceEquals(left, null) || Object.ReferenceEquals(right, null))
        {
            return false;
        }
        return left.A == right.A;
   }

   public override int GetHashCode(MyClass obj) {
        return obj?.A != null ? obj.A.GetHashCode() : 0;
   }
}

Böylece kullanılır:

var mset = new Dictionary<MyClass, MyClass>(new MyClassEquivalentAs());
var bye = new MyClass {A = "Hello", B = "Bye"};
var seeyou = new MyClass {A = "Hello", B = "See you"};
mset.Add(bye);

if (mset.Contains(seeyou)) {
    //something
}

MyClass item = mset[seeyou];
Console.WriteLine(item.B); // prints Bye

Anahtarla eşleşen bir nesneye sahip kodun, anahtar olarak kullanılan nesneye referansla değiştirilmesinin avantajlı olabileceği bir takım durumlar vardır. Örneğin, çok sayıda dizenin bir karma koleksiyondaki bir dizeyle eşleştirildiği biliniyorsa, tüm bu dizelere yapılan referansları koleksiyondaki dizilere yapılan referanslarla değiştirmek bir performans kazancı olabilir.
SuperCat

@supercat bugün bu bir ile elde edilir Dictionary<String, String>.
MikeFHay

@MikeFHay: Evet, ancak her bir dize başvurusunu iki kez saklamak zorunda kalmak biraz tuhaf görünüyor.
supercat

2
@supercat Aynı bir dize demek istiyorsan , bu sadece dize interning. Yerleşik eşyaları kullanın. Eğer bir çeşit "kanonik" temsil (yani büyük harf değiştirme teknikleri kullanılarak elde edilemeyen ) gibi bir terim kastediyorsanız , sizin gibi seslerin temelde bir indekse (DB'ler terimini kullandığı anlamında) ihtiyaç duyar. Her "kanonik olmayan formu" kanonik bir formla eşlenen anahtar olarak saklamakla ilgili bir sorun görmüyorum. ("Kanonik" form bir dize değilse, bunun eşit derecede iyi uygulandığını düşünüyorum.) Eğer bahsettiğiniz şey bu değilse, o zaman beni tamamen kaybettiniz.
jpmc26

1
Özel Comparerve Dictionary<MyClass, MyClass>pratik bir çözümdür. Java'da, aynısı TreeSetveya TreeMapartı özel olarak da yapılabilir Comparator.
Markus Kull

19

Sorununuz, iki çelişkili eşitlik kavramına sahip olmanızdır:

  • Tüm alanların eşit olduğu gerçek eşitlik
  • sadece A'nın eşit olduğu üyelik eşitliği

Kümenizdeki gerçek eşitlik ilişkisini kullanırsanız, belirli bir öğeyi kümeden alma sorunu ortaya çıkmaz - bir nesnenin kümede olup olmadığını kontrol etmek için, zaten o nesneye sahipsiniz. Bu nedenle, doğru eşitlik ilişkisini kullandığınızı varsayarsak, belirli bir örneği setten almak asla gerekli değildir.

Ayrıca bir iddia olabilir grubu bir bir soyut veri türü tarafından tamamen tanımlanmıştır S contains xya da x is-element-of Silgili ( “karakteristik fonksiyonu”). Başka işlemler istiyorsan aslında bir set aramıyorsun.

Oldukça sık gerçekleşen - ancak küme olmayan - tüm nesneleri farklı denklik sınıflarında gruplandırmamızdır . Her bir sınıf veya alt kümedeki nesneler sadece eşdeğerdir, eşit değildir. Her bir denklik sınıfını bu alt kümenin herhangi bir üyesi aracılığıyla temsil edebiliriz ve daha sonra bu temsil elemanının geri kazanılması istenir hale gelir. Bu , denklik sınıfından temsilci öğeye bir eşleme olacaktır .

C # 'da, bir sözlük açık bir eşitlik ilişkisini kullanabilir, sanırım. Aksi takdirde, böyle bir ilişki hızlı bir sarmalayıcı sınıfı yazarak uygulanabilir. pseudocode:

// The type you actually want to store
class MyClass { ... }

// A equivalence class of MyClass objects,
// with regards to a particular equivalence relation.
// This relation is implemented in EquivalenceClass.Equals()
class EquivalenceClass {
  public MyClass instance { get; }
  public override bool Equals(object o) { ... } // compare instance.A
  public override int GetHashCode() { ... } // hash instance.A
  public static EquivalenceClass of(MyClass o) { return new EquivalenceClass { instance = o }; }
}

// The set-like object mapping equivalence classes
// to a particular representing element.
class EquivalenceHashSet {
  private Dictionary<EquivalenceClass, MyClass> dict = ...;
  public void Add(MyClass o) { dict.Add(EquivalenceClass.of(o), o)}
  public bool Contains(MyClass o) { return dict.Contains(EquivalenceClass.of(o)); }
  public MyClass Get(MyClass o) { return dict.Get(EquivalenceClass.of(o)); }
}

"kümeden belirli bir örneği al" "Bence" örnek "i" üye "olarak değiştirdiysen, daha doğrudan ne demek istediğini iletirim. Sadece küçük bir öneri. =) +1
jpmc26

7

Ancak belirli bir öğeyi setten almak neden imkansız?

Çünkü setler bunun için değil.

Örneği tekrar değiştireyim.

"MyClass nesnelerini saklamak istediğim bir HashSet'im var ve nesnenin A özelliğine eşit olan A özelliğini kullanarak bunları elde etmek istiyorum".

"HashSet" yerine "Koleksiyon", "nesneler" "Değer" ve "A" özellikli "A" tuşuyla değiştirilirse, cümle şu şekilde olur:

"MyClass Değerlerini saklamak istediğim bir Koleksiyonum var ve nesnenin Anahtarına eşit olan Anahtarı kullanarak onları alabilmeyi istiyorum".

Tarif edilen bir Sözlük. Sorulan asıl soru "Neden HashSet'i Sözlük olarak göremiyorum?"

Cevap, aynı şey için kullanılmadıkları. Bir kümeyi kullanmanın nedeni, tek tek içeriğinin benzersizliğini garanti etmektir, aksi halde yalnızca bir Listeyi veya bir diziyi kullanabilirsiniz. Soruda tarif edilen davranış, bir Sözlük ne içindir. Tüm dil tasarımcıları batmadı. Bir get yöntemi sağlamazlar, çünkü eğer nesneye sahipseniz ve set içerisinde ise, bunlar eşdeğerdir, yani eşdeğer bir nesneyi "elde edeceksiniz" demektir. HashSet'in, eşit olmasını tanımladığınız eşdeğer olmayan nesneleri "alabilmeniz" sağlayacak şekilde uygulanması, diller bunu yapmanıza izin veren başka veri yapıları sağladığında başlangıç ​​değildir.

OOP ve eşitlik yorum / cevaplarıyla ilgili bir not. Eşlemenin anahtarının bir Sözlük'te depolanan değerin bir özelliği / üyesi olması sorun değildir. Örneğin: bir Kılavuza anahtar olarak sahip olmak ve eşittir metodu için kullanılan özellik tamamen makul. Mantıklı olmayan, özelliklerin geri kalanı için farklı değerlere sahip olmaktır. Eğer o yöne gidersem, muhtemelen sınıf yapımı yeniden düşünmem gerek.


6

Geçersiz kıldığınız anda hashcode'yu geçersiz kılmanız daha iyi olur. Bunu yaptıktan hemen sonra, "örnek" iniz bir daha asla iç durumunu değiştirmemelidir.

Eşittir ve karma kodunu geçersiz kılmazsanız, VM nesne kimliği eşitliği belirlemek için kullanılır. Bu nesneyi bir Küme'ye koyarsanız, onu tekrar bulabilirsiniz.

Eşitliği belirlemek için kullanılan bir nesnenin değerini değiştirmek, hash bazlı yapılarda bu nesnenin izlenemezliğine yol açacaktır.

Yani A üzerinde bir Setter tehlikelidir.

Şimdi eşitliklere katılmayan B'niz yok. Buradaki sorun anlamsal olarak teknik değil. Çünkü teknik olarak değişen B, eşitlik gerçeğine karşı nötrdür. Anlamsal olarak B "sürüm" bayrağı gibi bir şey olmalı.

Önemli olan:

Eğer A'ya eşit fakat B'ye eşit olmayan iki cisim varsa, bu cisimlerden birinin diğerinden daha yeni olduğu varsayımına sahipsin. Eğer B'nin sürüm bilgisi yoksa, bu varsayım algoritmada gizlidir. NE ZAMAN bu nesnenin bir Küme içinde "üzerine yazmaya / güncellemeye" karar verirsiniz. Bunun gerçekleştiği bu kaynak kodu konumu açık olmayabilir, böylece bir geliştirici, nesne X ile B'deki X'ten farklı olan nesne Y arasındaki ilişkiyi tanımlamakta zorlanacaktır.

Eğer B'nin sürüm bilgisi varsa, daha önce sadece koddan türetilmiş olan varsayımı ortaya koyarsınız. Şimdi görebilirsiniz, bu nesne Y, X'in daha yeni bir sürümü.

Kendinizi düşünün: Kimliğiniz tüm hayatınız boyunca kalır, belki bazı özellikler değişir (örneğin saçınızın rengi ;-)). Tabii eğer iki tane fotoğrafınız varsa, biri kahverengi saçlı, biri gri saçlı, fotoğrafta kahverengi saçlı olabileceğinizi varsayalım. Ama belki saçını renklendirdin? Sorun şu ki: SİZ saçınızı renklendirdiğinizi biliyor olabilirsiniz. Başkaları olabilir mi? Bunu geçerli bir bağlam içine koymak için mülk yaşını (versiyonunu) tanıtmanız gerekir. O zaman sen anlamsal ve açık konuşuyorsun

Gizli işlemden kaçınmak için "eskiyi yeni nesneyle değiştirmek" için bir setin bir get-metodu olmamalıdır. Böyle bir davranış istiyorsanız, eski nesneyi kaldırarak ve yeni nesneyi ekleyerek onu açık hale getirmelisiniz.

BTW: Almak istediğiniz nesneye eşit bir nesneye geçerseniz ne anlama gelir? Mantıklı değil. Teknik olarak kimse sizi engellemese de, semantiğinizi temiz tutun ve bunu yapma.


7
“Siz geçersiz kıldığınızda eşittir, hashcode'u daha iyi geçersiz kılarsınız. Bu ifade tam orada +100 değerinde.
David Arno

Değiştirilebilir duruma bağlı olarak eşitlik ve hashcode tehlikelerini belirtmek için +1
Hulk

3

Spesifik olarak Java'da, HashSetbaşlangıçta HashMapyine de kullanılarak uygulandı ve sadece değeri yoksaydı. Dolayısıyla ilk tasarım, bir getiri yöntemi sağlamada herhangi bir avantaj beklemiyordu HashSet. Eşit olan çeşitli nesneler arasında kanonik bir değer saklamak ve almak istiyorsanız, sadece HashMapkendiniz kullanın .

Ben bu yüzden vb C # şöyle dursun Ancak bu mantık hala Java tam olarak geçerli olup diyemeyiz, böyle uygulama detayları ile güncel tutulması olsanız bile HashSetdaha az bellek kullanmak reimplemented edildi HashMapher durumda, bu Setarayüze yeni bir yöntem eklemek için bir değişiklik olabilir . Bu yüzden herkesin sahip olmaya değer görmediği bir kazanım için oldukça acı verici.


Peki, Java’da bunu kesintisiz bir şekilde yapmak için bir uygulama sağlamak mümkün olabilirdefault . Sadece çok faydalı bir değişiklik gibi görünmüyor.
Hulk

@Hulk: Yanılıyor olabilirim, ancak ankete göre "Maddemi almanın tek yolu tüm koleksiyonu yinelemek ve tüm eşyaları eşitlik için kontrol etmektir" dediğinden, herhangi bir varsayılan uygulamanın kesinlikle verimsiz olacağını düşünüyorum. Öyleyse iyi bir nokta, bunu geriye uyumlu bir şekilde yapabilirsiniz, ancak elde edilen fonksiyonun O(n), karma fonksiyonu iyi bir dağılım gösterse bile karşılaştırmalarda çalışmayı garanti ettiği bir sonuç ortaya çıkarır . Daha sonra Setbunun da dahil olduğu arabirimdeki varsayılan uygulamanın geçersiz kılınması, HashSetdaha iyi bir garanti verebilir.
Steve Jessop

Kabul - İyi bir fikir olacağını sanmıyorum. Son zamanlarda List.sort eklenmiş varsayılan bir uygulama seçmek için - List.get (int index) veya - olsa da, bu tür bir davranış için öncelikler olacaktır . Arayüz tarafından maksimum karmaşıklık garantisi verilmektedir, ancak bazı uygulamalar diğerlerinden çok daha iyi olabilir.
Hulk

2

Setinde istediğiniz özelliğe sahip ana dil var.

C ++ std::set'da sıralı bir settir. Sağladığınız .findsipariş operatörüne <veya ikili bool(T,T)fonksiyonuna göre elementi arayan bir metoda sahiptir . İstediğiniz alma işlemini uygulamak için find komutunu kullanabilirsiniz.

Aslında, eğer bool(T,T) sağladığınız işlevin üzerinde belirli bir bayrak varsa ( is_transparent), işlevin aşırı yüklendiği farklı türde nesnelerde geçebilirsiniz . Bu, "sahte" verileri ikinci alana eklemeniz gerekmez, yalnızca kullandığınız sıralama işleminin arama ve ayarlanan türler arasında sipariş vermesini sağlayın.

Bu verimli bir izin verir:

std::set< std::string, my_string_compare > strings;
strings.find( 7 );

nerede my_string_comparenasıl önce bir dizeye tamsayı dönüştürmeden sipariş tamsayılar ve dizelerine (potansiyel maliyetle) anlar.

İçin unordered_set(C ++ 'ın karma seti), (henüz) hiçbir eşdeğer şeffaf bayrak yoktur. Bir de geçmelidir Tbir etmekunordered_set<T>.find yöntemle. Eklenebilir, ancak ==yalnızca bir sipariş gerektiren sıralı kümelerden farklı olarak, karma gerektirir ve bir karma .

Genel model, kabın aramayı yapması ve ardından kabın içindeki öğeye bir "yineleyici" vermesidir. Hangi noktada elementi setin içine alabilir veya silebilirsiniz, vb.

Kısacası, tüm dillerin standart konteynerleri tanımladığınız kusurlara sahip değildir. C ++ standart kütüphanesinin yineleyici tabanlı kapsayıcıları yoktur ve en azından bazı kaplar, tanımladığınız diğer dillerden herhangi birinden önce mevcuttu ve açıkladığınızdan bile daha verimli bir şekilde yapılabiliyordu . Tasarımınızda yanlış bir şey yok, ya da bu işlemi istemek; Kullandığınız Kümelerin tasarımcıları bu arayüzü sağlamıyordu.

C ++ standart kaplar, eşdeğer el haddelenmiş C kodunun düşük seviyeli işlemlerini temiz bir şekilde sarmak için tasarlandı; Yineleyicileri C tarzı işaretçilerden oluşan bir soyutlamadır. Bahsettiğiniz dillerin hepsi işaretçilerden bir kavram olarak uzaklaştı, bu yüzden yineleyici soyutlamayı kullanmadılar.

C ++ 'nın bu kusura sahip olmaması gerçeğinin bir tasarım kazası olması olasıdır. Yinelemeci merkezli yol, ilişkisel bir kaptaki bir öğeyle etkileşimde bulunmak için önce öğeye bir yineleyici elde edeceğiniz anlamına gelir; daha sonra kaptaki giriş hakkında konuşmak için bu yineleyiciyi kullanırsınız.

Bunun maliyeti, izlemeniz gereken yineleme geçersiz kılma kurallarının olmasıdır ve bazı işlemler için bir adım yerine 2 adım gerekir (bu, müşteri kodunu daha gürültülü yapar). Bunun yararı, sağlam soyutlamanın, API tasarımcılarının orijinal olarak düşündüklerinden daha gelişmiş kullanıma izin vermesidir.

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.