C # 7'de bir “Dene” yöntemi yazmanın en zarif yolu nedir?


21

İşlem başarılı olursa boole değeri döndürdüğüm, TryDequeueçeşitli .NET TryParseyöntemlerine benzer bir kalıp kullanan bir yönteme sahip bir tür Queue uygulaması yazıyorum outve fiili boşaltılmış değeri döndürmek için bir parametre kullanıyorum .

public bool TryDequeue(out Message message) => _innerQueue.TryDequeue(out message);

Şimdi, outne zaman mümkün olursa paramlardan kaçınmayı seviyorum . C # 7, onlarla çalışmayı kolaylaştırmak için bize değişken sınırlamalar veriyor, ancak params'leri yararlı bir araçtan daha gerekli bir kötülükten daha çok düşünüyorum.

Bu yöntemden istediğim davranış aşağıdaki gibidir:

  • Çıkarılacak bir öğe varsa, onu iade edin.
  • Temizlenecek bir öğe yoksa (sıra boş), arayan kişiye uygun şekilde davranması için yeterli bilgiyi sağlayın.
  • Kalan öğe yoksa, yalnızca boş bir öğeyi döndürmeyin.
  • Boş bir sıradan ayrılmaya çalışıyorsanız, bir istisna atmayın.

Şu anda, bu yöntemin bir arayanı hemen hemen her zaman aşağıdaki gibi bir kalıp kullanır (C # 7 çıkış değişkenini kullanarak):

if (myMessageQueue.TryDequeue(out Message dequeued))
    MyMessagingClass.SendMessage(dequeued)
else
    Console.WriteLine("No messages!"); // do other stuff

En kötüsü değil, hepsi söyledi. Ama yardım edemem ama bunu yapmanın daha güzel yolları olabileceğini hissediyorum (olamayacağını kabul etmeye tamamen istekliyim). Arayanın nasıl ayrılması gerektiğinden nefret ediyorum, eğer varsa tek istediği bir değer elde etmek istediğinde koşullu bir akış.

Bu aynı "denemek" davranışını gerçekleştirmek için var olan diğer bazı desenler nelerdir?

Bağlam için, bu yöntem potansiyel olarak VB projelerinde çağrılabilir, bu nedenle her ikisinde de işe yarayan bir şey için bonus Puan. Bu gerçek olsa da, çok az ağırlık taşımalıdır.


9
Bir Option<T>yapı tanımlayın ve geri döndürün. Bu bool Try(..., out data)işlevler bir tükenmedir.
CodesInChaos 16:17

2
Benzer düşünüyordum ... Belki OUT parametrelerine ters ise <T>, Seçenek <T>.
Jon Raynor

1
@FrustratedWormsDesigner ile iyi, başkalarını çok denedim "demedim (püf noktaları açıktır), onlar kadar düşündüm. Bir ValueTuple döndürme fikrim vardı ama en iyisi bunun daha fazla gelişme sağlayacağını sanmıyorum.
Eric Sondergard

@CodesInChaos Aynı sayfadayız, bu yüzden buradayım! Ve bu fikri sevdim. Vaktiniz varsa, cevap olarak kabul edebilmem için bir cevapta daha fazla ayrıntı vermek ister misiniz?
Eric Sondergard

Yanıtlar:


24

Genellikle "Bazı" (bir değer olduğunda) veya "Yok" (bir değer olmadığında) olarak adlandırılan iki sürümü olan bir tür nesnesi demek için bir Seçenek türü kullanın. Onlar Just Just Nothing denir. Daha sonra bu türlerde, varsa değere erişmenizi, varlığını test etmenizi ve en önemlisi, varsa değerine başka bir Seçenek döndüren bir işlevi uygulamanızı sağlayan bir yöntem vardır (genellikle C # Diğer dillerde genellikle Bind olarak adlandırılmasına rağmen FlatMap olarak adlandırılır ... Bu isim ve tipe sahip olmanızın nedeni, LINQ deyimlerinde Option nesnelerinizi kullanmanıza izin vermesidir.

Ek özellikler, ilgili koşullardaki eylemleri çağırmak için IfPresent ve IfNotPresent ve OrElse (hiçbir değer olmadığında varsayılan bir değer yerine ancak başka bir işlem yapılmazsa yerine), vb. Gibi yöntemler içerebilir.

Örneğin, örneğiniz şöyle bir şeye benzeyebilir:

myMessageQueue.TryDeque()
    .IfPresent( dequeued => MyMessagingClass.SendMessage(dequeued))
    .IfNotPresent (() =>  Console.WriteLine("No messages!")

Bu, Option (veya Belki) monad modelidir ve son derece kullanışlıdır. Mevcut uygulamalar var (örneğin, https://github.com/nlkl/Optional/blob/master/README.md ), ancak sizin için de zor değildir.

(Bu deseni genişletmek isteyebilirsiniz, böylelikle yöntem başarısız olduğunda hiçbir şeyin yerine hata sebebinin bir tanımını geri gönderebilirsiniz ... Bu tamamen elde edilebilirdir ve genellikle Her iki monad olarak adlandırılır; Bu durumda da çalışmayı kolaylaştırmak için desen)


Bu kesinlikle iyi bir seçenek, öneri ve düşünceleriniz için teşekkürler. Gerçekten burada daha fazla fikir keşfetmeyi düşünüyordum.
Eric Sondergard

2
Güzel bir şey hakkında Option/ Maybezamankinden doğrudan farklı davalarını gerek kalmadan, güvenle, açılmamış, zincirleme sarılmış edilebileceğini hangi araçları (tabii ki tek olarak uygulanan varsa) ve işlenmiş bir monad olmasıdır, ve bu zincirleme ve LINQ Sorgu İfadeleri ile işlem yapılabilir.
Jörg W Mittag

3
Bu arada, flatMap/ .NET binddenir SelectMany.
Jörg W Mittag

5
Acaba farklı bir isim seçmenin daha iyi bir fikir olup olmayacağını merak ediyorum, çünkü bunu yaparak Try....NET'te adlandırma kurallarını çiğniyorsunuz (ve potansiyel olarak kafa karıştırıcıları koruyorsunuz).
Bob

@Bob Bu harika bir nokta. Katılıyorum.
Eric Sondergard

12

Verilen cevaplar iyi ve onlardan biriyle giderdim. Bu cevabı, sadece birkaç köşeyi alternatif fikirlerle dolduruyor gibi düşünün:

  • MessageDenilen SomeMessageve alt sınıflarını yapın NoMessage. Dequeueşimdi bir mesaj yoksa NoMessageve SomeMessagebir mesaj varsa geri dönebilirsiniz . Çalan hangi durumda olduğunu tespit etmeye özen gösterirse, bunu tip incelemesi ile kolayca yapabilirler. Eğer yapmazlarsa NoMessage, yöntemlerinden herhangi biri çağrıldığında hiçbir şey yapmazlar ve hey, istediklerini elde ederler.

  • Yukarıdakilerle aynıdır, ancak Messageörtük olarak dönüştürülebilir hale getirilebilir bool(veya uygular operator true / operator false). Şimdi, if (message)eğer mesajın kötüyse iyi, yanlış ise doğru olup olmadığını söyleyebilirsiniz. (Bu neredeyse kesinlikle kötü bir fikirdir, ancak bütünlüğüne dahil ediyorum!)

  • C # 7'de perdeler var. Bir (bool, Message)demet döndür.

  • Yap Messagebir yapı tipini ve getiri Message?.


Hey, cevabın için teşekkürler. Bu soru ile kendime özel bir sorunuma çözüm bulmaya çalışırken kendimi farklı düşüncelere maruz bırakmaya çalışıyordum. Şerefe!
Eric Sondergard

Yani, "kötü fikriniz" Birlik tarafından kullanılıyorsa, neden kullanamıyoruz?
Arturo Torres Sánchez

@ ArturoTorresSánchez: Sorunuz "başkası gerçekten kötü bir programlama uygulaması kullandı, öyleyse neden yapamıyorum?" Soru tutarsız. Hiç kimse sizi aynı kötü programlama uygulamalarını başkasıyla kullanmanızı engellemiyor. Sen devam et. Bu C # 'da iyi bir uygulama yapmaz.
Eric Lippert

Yanakta dildi. Sanırım işaretlemek için "/ s" kullanmam gerekiyor.
Arturo Torres Sánchez

@ ArturoTorresSánchez: Bu bir soru-cevap sitesidir; Ben olmak soruları dikkate sorular arıyorlar cevapları .
Eric Lippert

10

C # 7'de aynı şeyi daha şık bir şekilde elde etmek için desen eşleştirmeyi kullanabilirsiniz:

if (myMessageQueue.TryDequeue() is Message dequeued) 
{
     MyMessagingClass.SendMessage(dequeued)
} 
else 
{
    Console.WriteLine("No messages!"); // do other stuff
}

Yine de bu özel durumda, kuyruğu tekrar tekrar sorgulamak yerine muhtemelen olayları kullanırdım.


3
Bununla TryDequeuebirlikte, bir Messageuygulama ile bir marker arayüzü ve bir "hiçbir şey" uygulaması ile geri dönmesi gerekmiyor mu ? Tabii ki, çağrı sitesinde daha şık, ancak Messagemevcut bir tür olması halinde , kendisinin imkansız olabileceği gerçek kodda 3 uygulama yapmak zorunda kalıyorsunuz .
Telastyn

1
@Telastyn: Ayrıca sadece null döndürürse çalışır . Bir Seçenek türü de kullanabilirsiniz.
JacquesB

@ JacquesB: ama ya geçerli bir sıraya koyulabilir öğe ise null?
JBSnorro

5

Başarısızlığın (değer yok) başarının olduğu kadar yaygın olduğu bir senaryo için Try ... yönteminde (out parametresi olan) yanlış bir şey yoktur.

Ancak, farklı şeyler yapmakta ısrar ediyorsanız, boş bir mesaj döndürmek veya yalnızca bunu göndermek (artık sorun değil) göndermek veya içerikli bir mesajınız olup olmadığını gerçekten anlayana kadar if ifadesini ertelemek isteyebilirsiniz.

Yine de birinin testi yapması gerektiğini unutmayın. Testin, sorunun ne zaman ve nerede yapıldığı tartışılmalıdır. Bu, temizleme zamanıdır.


İyi bir noktaya değindin. Ne yaparsan yap hala test etmelisin Dürüst olmak gerekirse, şu anda hala parametrelerden çıkıyorum olabilir (aslında şu anda birden fazla "TryDequeue" uygulamasını şu anda sadece her biriyle başa çıkmak için kullanıyorum). Gerçekten sadece orada olabilecek diğer seçenekleri tartışmak istedim.
Eric Sondergard
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.