Yöntemlerimde üzerlerinde yinelenen boş koleksiyonları kabul etmeli miyim?


40

Tüm mantığın, yöntemin parametresi üzerinde yinelenen bir foreach döngüsü içinde gerçekleştirildiği bir yöntemim var:

public IEnumerable<TransformedNode> TransformNodes(IEnumerable<Node> nodes)
{
    foreach(var node in nodes)
    {
        // yadda yadda yadda
        yield return transformedNode;
    }
}

Bu durumda, boş bir koleksiyon göndermek boş bir koleksiyonla sonuçlanır, ancak bunun yanlış olup olmadığını merak ediyorum.

Buradaki mantığım, eğer birileri bu yöntemi çağırıyorsa, o zaman verileri aktarmak niyetindedir ve hatalı durumlarda yalnızca benim yöntemime boş bir koleksiyon geçmesi niyetindedir.

Bu davranışı yakalamalı mıyım ve bunun için bir istisna atmalı mıyım, yoksa boş koleksiyonu iade etmek en iyi yöntem midir?


43
"Birisi bu yöntemi çağırıyorsa, o zaman veri iletmeyi planlıyorlarsa" doğru olduğuna emin misiniz? Belki de sonuçta çalışmak için ellerinden geleni geçiyorlar.
SpaceTrucker

7
BTW: "dönüştürme" yönteminiz genellikle "harita" olarak adlandırılır, ancak .NET'te (ve .NET'in adını aldığı SQL), genellikle "seçme" olarak adlandırılır. "Dönüştürme", C ++ dediği şeydir, ancak bu tanıyabileceği ortak bir ad değildir.
Jörg W Mittag

25
Koleksiyon nullboş olsa da olmasa fırlatırdım .
KodlarInChaos

21
@NickUdell - Kodlarımda her zaman boş koleksiyonları dolaşıyorum. Koleksiyonumla devam etmeden önce koleksiyonuma hiçbir şey ekleyemediğim durumlar için özel dallanma mantığı yazmaktan aynı şekilde davranmak daha kolaydır.
Mayer

7
Koleksiyon boşsa bir hatayı kontrol edip atarsanız, arayanı da boş bir tahsilatı yönteminize göre kontrol edip geçmemesi için zorlarsınız. Onaylar, (ve sadece açık) sistem sınırlarında yapılmalı, eğer boş koleksiyonlar sizi rahatsız ediyorsa, herhangi bir kullanım yöntemine ulaşmadan çok önce kontrol etmiş olmalısınız. Şimdi iki gereksiz çekiniz var.
Yalan Ryan

Yanıtlar:


175

Faydalı yöntemler boş koleksiyonlara atılmamalıdır. API istemcileriniz sizden nefret eder.

Bir koleksiyon boş olabilir; “boşalmaması gereken bir koleksiyon”, kavramsal olarak çalışılması gereken çok daha zor bir şeydir.

Boş bir koleksiyonun dönüştürülmesinin açık bir sonucu vardır: boş koleksiyon. (Parametreyi geri döndürerek biraz çöp bile kaydedebilirsiniz.)

Bir modülün zaten bir şeyle dolu olan veya olmayan şeylerin listesini tuttuğu birçok durum vardır. Her çağrıdan önce boşluk olup olmadığını kontrol etmek transformcan sıkıcıdır ve basit, zarif bir algoritmayı çirkin bir karmaşaya çevirme potansiyeline sahiptir.

Fayda yöntemleri her zaman girdilerinde liberal olmak ve çıktılarında muhafazakar olmak için çaba sarf etmelidir.

Tüm bu nedenlerden dolayı, Tanrı aşkına, boş koleksiyonu doğru şekilde kullanın. Hiçbir şey, senden daha iyi istediğini bildiğini sanan bir yardımcı modülden daha fazla yorucu olamaz .


14
+1 - (eğer yapabilseydim daha fazlası). nullBoş koleksiyonda birleşecek kadar ileri gidebilirim bile . Örtük sınırlamaları olan işlevler bir acıdır.
Telastyn

6
"Hayır, yapmamalısın" ... onları kabul etmemeli mi veya istisna atmamalı mı?
Ben Aaronson

16
@Andy - bunlar faydalı yöntemler olamazdı . İş yöntemleri ya da buna benzer somut davranışlar olabilirler.
Telastyn

38
Boş bir tahsilatı geri dönüş için geri dönüştürmenin iyi bir fikir olduğunu sanmıyorum. Sadece çok küçük bir hafıza biriktiriyorsunuz; ve arayanda beklenmeyen davranışlara yol açabilir. Biraz tartışılan bir örnek: Populate1(input); output1 = TransformNodes(input); Populate2(input); output2 = TransformNodes(input); Populate1 koleksiyonu boş bıraktıysa ve ilk TransformNodes araması için geri getirdiyseniz, output1 ve input aynı koleksiyon olacaktı ve Populaten2 çağrıldığında koleksiyona düğüm koyarsanız ikincinizle sonuçlanacaktı. Çıkış2'deki giriş kümesi.
Dan Neely

6
@Andy Öyle olsa bile, eğer argüman hiç boş olmamalıysa - işlenecek veri yoksa - o zaman bu argümanı harmanlarken bunu kontrol etmek arayanın sorumluluğunu hissediyorum . Yalnızca bu yöntemi daha zarif tutmakla kalmaz, aynı zamanda daha iyi bir hata mesajı (daha iyi bağlam) oluşturabilir ve onu hızlı bir şekilde artırabilir. Bir alt sınıf için arayanın değişmeyenlerini doğrulaması mantıklı değil ...
Andrzej Doyle

26

Bunun cevabını belirleyen iki önemli soru görüyorum:

  1. İşleviniz boş bir koleksiyondan ( null dahil ) geçtiğinde anlamlı ve mantıklı bir şey getirebilir mi?
  2. Bu uygulama / kütüphane / ekipteki genel programlama tarzı nedir? (Özellikle, ne kadar FP'sin?)

1. Anlamlı getiri

Temel olarak, anlamlı bir şey döndürebilirseniz, bir istisna atmayın. Arayanın sonucu ile ilgilenmesine izin verin. Yani eğer işleviniz ...

  • Koleksiyondaki öğelerin sayısını sayar, 0 döndürür. Bu kolaydı.
  • Belirli ölçütlere uyan öğeleri arar, boş bir koleksiyon döndürür. Lütfen hiçbir şey atmayın. Arayan, bazıları boş, bazıları olmayan büyük bir koleksiyona sahip olabilir. Arayan, herhangi bir koleksiyondaki eşleşen öğeleri ister. İstisnalar, yalnızca arayanın hayatını zorlaştırır.
  • Listedeki en büyük / en küçük / en iyi kritere uygun olanı arıyor. Hata. Stil sorusuna bağlı olarak, burada bir istisna atabilir veya boş verebilirsiniz . Boş değerden nefret ediyorum (bir FP kişisi) ama burada tartışmasız daha anlamlı ve kendi kodunuzda beklenmeyen hatalar için istisnalar ayırmanıza izin veriyor. Arayan kişi null değerini kontrol etmezse, yine de oldukça net bir istisna ortaya çıkar. Ama bunu onlara bırakıyorsun.
  • Koleksiyondaki nt maddeyi veya ilk / son n maddeyi sorar. Bu bir istisna için henüz en iyi durumdur ve arayanlar için kafa karışıklığına ve zorluğa neden olma ihtimali en düşük olanıdır. Bir dava halen için yapılabilir boş sizin ve ekibinizin önceki noktada verilen tüm nedenlerle, bu kontrol için kullanılıyorsa, ama bu bir atma için henüz en geçerli durum DudeYou bilir YouShouldCheckTheSizeFirst istisna. Stiliniz daha işlevselse, Stil cevabımı boş bırakın veya okuyun .

Genel olarak, AP önyargım bana “anlamlı bir şey geri çevir” diyor ve null bu durumda geçerli bir anlam ifade ediyor olabilir.

2. Stil

Genel kodunuz (veya projenin kodu veya ekibin kodu) işlevsel bir tarzı mı destekliyor? Hayır ise, istisnalar beklenir ve ele alınır. Eğer evet ise, O zaman bir Seçenek Türü döndürmeyi düşünün . Bir seçenek türüyle, anlamlı bir cevap ya da Yok / Hiçbiri döndürürsünüz . Yukarıdaki üçüncü örneğime göre, FP tarzında hiçbir şey iyi bir cevap olamazdı. İşlevin bir Seçenek türünü döndürmesi, arayan kişiye anlamlı bir cevaptan daha açık bir şekilde verilebilir ve arayanın bununla başa çıkmaya hazır olması gerekir. Arayan kişiye daha fazla seçenek sağladığını düşünüyorum (cezayı affedecekseniz).

Tüm serin Net çocuklar bu tür şeyleri yapmak burada F # ama C # yapar destekleyen bu tarzı.

tl; Dr.

Başka birinden tamamen öngörülemeyen (ve yasal) girdiler değil, kendi kod yolunuzdaki beklenmeyen hatalar için istisnaları saklayın.


"En uygun" vb.: Bazı kriterlere uyan bir koleksiyondaki en uygun nesneyi bulan bir metoda sahip olabilirsiniz. Bu yöntem boş olmayan koleksiyonlar için de aynı problemi doğurur; çünkü hiçbir şey kritere uymaz, bu yüzden boş seçimler için endişelenmenize gerek yoktur.
gnasher729

1
Hayır, bu yüzden ¨ en uygun ’dedim ve ¨ eşleşmedi ¨; ifade, tam eşleşmeyi değil en yakın yaklaşımı ifade eder. En büyük / en küçük seçenekler bir ipucu olmalıydı. Sadece “en uygun” talep edildiyse, 1 veya daha fazla üyesi olan herhangi bir koleksiyon bir üyeye geri dönebilmelidir. Sadece bir üye varsa, bu en uygun seçimdir.
59'da

1
Çoğu durumda "null" döndürmek bir istisna atmaktan daha kötüdür. Ancak boş bir koleksiyonun iade edilmesi anlamlıdır.
Ian

1
Tek bir öğeyi iade etmeniz gerekiyorsa, (min / max / kafa / kuyruk). Null'a gelince, dediğim gibi, ondan nefret ediyorum (ve olmayan dilleri tercih ediyorum), fakat bir dilin olduğu yerde, onunla başa çıkabilmelisin ve onu dağıtan bir kod yazmalısın. Yerel kodlama sözleşmelerine bağlı olarak uygun olabilir.
18'de

Örneklerin hiçbirinin boş bir girdiyle ilgisi yok. Onlar sadece cevabın "hiçbir şey" olabileceği özel durumlar . Sırasıyla OP'nin boş koleksiyonun nasıl "ele alınacağı" sorusuna cevap vermesi gerekir.
djechlin

18

Her zamanki gibi, buna bağlı.

Koleksiyonun boş olması önemli mi?
Koleksiyon işleme kodlarının çoğu muhtemelen "hayır" derdi; Bir koleksiyon, içinde sıfır dahil olmak üzere herhangi bir sayıda öğeye sahip olabilir .

Şimdi, içinde hiçbir öğenin bulunmasının "geçersiz" olduğu bir koleksiyonunuz varsa, bu yeni bir gereksinimdir ve bu konuda ne yapacağınıza karar vermeniz gerekir.

Veritabanı dünyasından bir miktar test mantığı ödünç al: sıfır öğe, bir öğe ve iki öğe için test yap . Bu, en önemli vakalara hitap eder (kötü şekillendirilmiş iç veya kartezyen birleşme koşullarını siler).


Bir koleksiyonda hiçbir öğeye sahip olmak geçersizse, ideal olarak argüman tutarlı bir şekilde koruyabilen ve başlangıçta geçersiz bir duruma girmenizi engelleyebilecek java.util.Collectionözel bir com.foo.util.NonEmptyCollectionsınıf olamaz.
Andrzej Doyle,

3
Bu soru [c #] olarak etiketlendi
Nick Udell

@NickUdell, C # Nesneye Yönelik programlama veya iç içe isim alanları kavramını desteklemiyor mu? TIL.
John Dvorak

2
Öyle. Benim yorumum Andrzej'in kafası karışmış olacağı için açıklığa kavuştu (neden bu soruya referans vermeyen bir dil için isim-boşluk belirtme çabasına gittiler?).
Nick Udell

-1 “neredeyse kesinlikle evet” den daha ““ buna bağlı ”olduğunu vurgulamak için. Şaka yok - yanlış şeyi iletmek burada zarar verir.
djechlin

11

İyi bir tasarım meselesi olarak, girdilerinizde mümkün olduğu kadar mümkün olan en fazla çeşitliliği kabul edin. İstisnalar yalnızca (kabul edilemez bir girdi sunulduğunda VEYA işleme sırasında beklenmeyen hatalar oluştuğunda) atılmalıdır ve program bunun sonucunda öngörülebilir bir şekilde devam edemez .

Bu durumda, boş bir koleksiyonun sunulması beklenir ve kodunuzun (zaten yaptığı gibi) ele alması gerekir. Kodunuz burada bir istisna attıysa, iyi olanın ihlali olur. Bu matematikte 0 ile 0 arasında çarpma yapmaya benzer. Gereksiz, ancak kesinlikle olduğu gibi çalışması için gerekli.

Şimdi, boş toplama argümanına. Bu durumda boş bir toplama bir programlama hatasıdır: programcı bir değişken atamayı unuttu. Bu, bir istisna atılabileceği bir durumdur, çünkü bunu bir çıktıya anlamlı bir şekilde işleyemezsiniz ve bunu yapmaya çalışmak beklenmeyen davranışlara yol açar. Bu matematikte sıfıra bölmeye benzerdi - tamamen anlamsız.


Kalın ifadeniz, genel olarak son derece kötü bir tavsiyedir. Burada doğru olduğunu düşünüyorum, ancak bu durumla ilgili neyin özel olduğunu açıklamanız gerekiyor. (Genel olarak kötü bir tavsiye, çünkü sessiz başarısızlıkları teşvik eder.)
djechlin

@AAA - açıkça burada yazılımı nasıl tasarlayacağımızla ilgili bir kitap yazmıyoruz, ancak ne dediğimi gerçekten anlıyorsanız bunun hiç de fena bir tavsiye olduğunu sanmıyorum. Demek istediğim, kötü girdilerin belirsiz veya keyfi çıktılar ürettiğidir, bir istisna atmanız gerekir. Çıktının tamamen tahmin edilebilir olduğu durumlarda (burada olduğu gibi), bir istisna atmak keyfi olacaktır. Kilit nokta, tahmin edilemez davranışların ortaya çıktığı haliyle, programınızda hiçbir zaman keyfi kararlar vermemektir.
Mayer

Ama bu senin ilk cümlen ve en çok vurgulananlardan biri ... henüz ne söylediğini kimse anlamadı. (Burada çok agresif olmaya çalışmıyorum, burada yapacağımız şeylerden sadece biri yazmayı ve iletişim
kurmayı öğreniyor

10

Doğru çözüm sadece işlevinize izolasyonlu olarak baktığınızda görmek çok daha zordur. İşlevinizi daha büyük bir sorunun parçası olarak kabul edin . Bu örneğe olası bir çözüm şunun gibi görünüyor (Scala'da):

input.split("\\D")
.filterNot (_.isEmpty)
.map (_.toInt)
.filter (x => x >= 1000 && x <= 9999)

İlk önce dizgiyi basamaksız olarak bölün, boş dizgileri filtreleyin, dizgileri tam sayılara dönüştürün, sonra sadece dört basamaklı sayıları tutmak için filtreleyin. İşleviniz map (_.toInt)boru hattında olabilir.

Bu kod oldukça basittir, çünkü boru hattındaki her aşama sadece boş bir dize veya boş bir koleksiyonu ele alır. Başında boş bir dize koyarsanız, sonunda boş bir liste çıkar. nullHer aramadan sonra durmanız ve kontrol etmeniz veya istisna yapmanız gerekmez .

Tabii ki, boş bir çıktı listesinin birden fazla anlamı olmadığı varsayılmaktadır. Eğer varsa gerek kendisini dönüştürmek, bu tamamen değiştiren şeyler neden boş boş girişe neden çıktı ve biri arasında ayrım yapmak.


Son derece etkili bir örnek için +1 (diğer iyi cevaplarda yoktur).
djechlin

2

Bu soru aslında istisnalar hakkında. Bu şekilde bakarsanız ve boş koleksiyonu bir uygulama detayı olarak görmezden gelirseniz, cevap açıktır:

1) Bir yöntem, devam edemediğinde bir istisna atmalıdır: belirlenmiş görevi gerçekleştirememek veya uygun değer döndürmek.

2) Bir yöntem, başarısızlığa rağmen devam edebildiği zaman bir istisna yakalamalıdır.

Yani, yardımcı yöntem "faydalı" ve sürece bir istisna olmamalı bunun boş bir koleksiyonu ile 's işi yapmak edememektedir. Arayanın, sonuçların ele alınıp kullanılamayacağını belirlemesine izin verin.

Boş bir koleksiyonun geri verilmesi ya da null olması, biraz daha zordur, ancak fazla değildir: eğer mümkünse null koleksiyonlarından kaçınılmalıdır. Boşaltılan bir koleksiyonun amacı, (SQL'de olduğu gibi) bilgiye sahip olmadığınızı belirtmek olacaktır; örneğin, bir çocuk koleksiyonu varsa, eğer birisinin olup olmadığını bilmiyorsanız, boş olabilir. bilmediklerini biliyorum. Ancak, eğer bir nedenden ötürü önemliyse, muhtemelen onu takip etmek için ekstra bir değişkene değer.


1

Yöntemin adı TransformNodes. Girdi olarak boş bir koleksiyon olması durumunda, boş bir koleksiyonun geri alınması doğal ve sezgiseldir ve mükemmel matematiksel anlam ifade eder.

Eğer yöntem Maxmaksimum öğeyi döndürmek için adlandırılmış ve tasarlanmışsa, NoSuchElementExceptionboş bir koleksiyona atmak doğaldır , çünkü hiçbir şey maksimum değildir ve matematiksel bir anlam ifade etmez.

Metot adlandırılmış JoinSqlColumnNamesve elemanların SQL sorgularında kullanmak üzere virgülle birleştirildiği bir dize döndürmek için tasarlandıysa IllegalArgumentException, arayan kişi sonunda kullandığı takdirde bir SQL hatası alacağı için boş bir koleksiyona atmak mantıklı olacaktır. bir SQL sorgusu içindeki dizgede başka bir denetime gerek kalmadan ve geri döndürülen bir boş dize denetlemek yerine, gerçekten boş toplama olup olmadığını kontrol etmesi gerekirdi.


Maxhiçbir şey genellikle negatif sonsuzluktur.
djechlin

0

Geri adım atalım ve bir değer dizisinin aritmetik ortalamasını hesaplayan farklı bir örnek kullanalım.

Giriş dizisi boşsa (veya boşsa), arayanın isteğini makul şekilde yerine getirebilir misiniz? Hayır. Seçenekleriniz neler? Peki, yapabilirsin:

  • şimdiki zaman / geri dönüş / hata atma. kod tabanınızın kuralını bu hata sınıfı için kullanarak.
  • sıfır gibi bir değerin döndürüleceğini belgelemek
  • belirtilen geçersiz bir değerin iade edileceğini gösteren belge (örneğin NaN)
  • sihirli bir değerin döndürüleceğini belgeleyin (örneğin, tür için bir min veya max veya umarım belirleyici bir değer)
  • sonucun belirtilmemiş olduğunu beyan etmek
  • eylemin tanımsız olduğunu beyan etmek
  • vb.

Size geçersiz girdiler sağladılarsa ve istek tamamlanamazsa onlara hatayı ver diyorum. İlk günden itibaren zor bir hata demek, programınızın gereksinimlerini anlamaları. Sonuçta, işleviniz cevap verecek konumda değil. Operasyon olursa olabilir (örneğin bir dosyayı kopyalamak) başarısız, daha sonra API onlara başa çıkabilirim bir hata vermelidir.

Böylece, kütüphanenizin hatalı biçimlendirilmiş istekleri ve başarısız olabilecek istekleri nasıl ele alacağını tanımlayabilirsiniz.

Kodunuzun, bu hata sınıflarını işleme biçiminde tutarlı olması çok önemlidir.

Sonraki kategori kütüphanenizin saçma istekleri nasıl ele alacağına karar vermek. Sizinkine benzer bir örneğe geri alınıyor - Bir dosyanın bir yolda bulunduğunu belirler bir işlevi kullanalım: bool FileExistsAtPath(String). İstemci boş bir dize geçirirse, bu senaryoyu nasıl ele alırsınız? Boş ya da boş bir dizinin geçmesine ne dersiniz void SaveDocuments(Array<Document>)? Kitaplığınıza / kod tabanınıza karar verin ve tutarlı olun. Bu davaların hatalarını göz önünde bulunduruyorum ve istemcilerin hata olarak işaretleyerek saçma taleplerde bulunmalarını yasaklıyorum (bir iddia ile). Bazı insanlar bu fikre / eyleme şiddetle karşı çıkacaktır. Bu hata tespitini çok faydalı buluyorum. Sorunları programlara yerleştirmek için çok iyidir - rahatsız edici programa iyi bir konuma sahip. Programlar çok daha net ve doğru (kod tabanınızın gelişimini düşünün) ve hiçbir şey yapmayan fonksiyonlar içinde döngüleri yazma. Kod bu şekilde daha küçük / temizleyicidir ve kontroller genellikle sorunun verilebileceği yerlere itilir.


6
Son örneğiniz için, saçma olanı belirlemek, bu işlevin işi değildir. Bu nedenle, boş bir dize false döndürmelidir. Aslında, bu File.Exists'in aldığı yaklaşımdır .
Mayer

@ rmayer06 bu onun işi. başvurulan işlev null değerine, boş dizgilere izin verir, dizgideki geçersiz karakterleri denetler, dizginin kısaltılmasını gerçekleştirir, dosya sistemi çağrıları yapmanız gerekebilir, çevreyi sorgulamanız gerekebilir, vb. yüksek maliyet ve kesinlikle doğruluk çizgilerini (IMO) bulanıklaştırıyorlar. if (!FileExists(path)) { ...create it!!!... }Birçoğu işlenmeden önce yakalanacak birçok hata gördüm , doğruluk çizgileri bulanık değildi.
Justin

2
İşlevin adı File.ThrowExceptionIfPathIsInvalid ise, ben de aynı fikirdeyim, ama onların aklında kim böyle bir işlev görür?
Mayer

İşlevin nasıl tanımlandığı nedeniyle, ortalama 0 öğe zaten NaN döndürecek ya da sıfıra bölünmüş bir istisna atacak. Muhtemelen bulunamayan, ya varolmayacak olan ya da zaten bir hatayı tetikleyebilecek olan bir dosya. Bu davaları özel olarak ele almak için zorunlu bir sebep yok.
cHao

Hatta bir dosyayı okumadan veya yazmadan önce bir dosyanın varlığını kontrol etme kavramının yine de kusurlu olduğu iddia edilebilir. (İçsel bir yarış durumu vardır.) Sadece devam etmek ve dosyayı açmak, okumak için ya da yazmak için yalnızca oluştur ayarları ile açmayı denemek daha iyidir. Dosya adı geçersizse veya mevcut değilse (veya yazma durumunda varsa) zaten başarısız olur.
cHao

-3

Genel bir kural olarak, standart bir işlev en geniş girdi listesini kabul edebilmeli ve bu konuda geri bildirim verebilmelidir, programcıların işlevleri tasarımcıların planlamadığı şekilde kullandığı birçok örnek vardır, bunun akılda tutulduğunu düşünüyorum. yalnızca boş koleksiyonları değil, aynı zamanda çok çeşitli giriş türlerini de kabul edebilmeli ve girişte yapılan işlemlerden bir hata nesnesi olsa bile ...


Tasarımcının bir koleksiyonun daima geçileceğini ve doğrudan söz konusu koleksiyon üzerinde yinelemeye gittiğini varsayması kesinlikle yanlıştır, alınan argümanın beklenen veri tipine uygunluğunu sağlamak için bir kontrol yapılması gerekir ...
Clement Mark-Aaba

3
"geniş çeşitlilikteki giriş tipleri" burada önemsiz görünüyor. Soru, kesinlikle yazılmış bir dil olan c # olarak etiketlenir . Yani, derleyici girdinin bir koleksiyon olduğunu garanti eder
gnat

Nazikçe cevabım programcı olarak siz öngörülemeyen veya hatalı olaylar için oda, bunlardan biri yanlışlıkla geçirilebilir fonksiyon argümanları genelleştirilmiş dikkatli olun ki, "başparmak üstünlüğünü" google ...
Clement Mark-Aaba

1
Bu ya yanlış ya da totolojiktir. writePaycheckToEmployeeOlumsuz bir sayıyı girdi olarak kabul etmemelisiniz ... ancak "geri bildirim", "sonradan olabilecek bir şey yaparsa" anlamına gelirse, o zaman evet, her işlev bir sonraki işlemi yapar.
djechlin
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.