C # jeneriklerinde geçersiz?


98

İstek alan ve yanıt veren genel bir yöntemim var.

public Tres DoSomething<Tres, Treq>(Tres response, Treq request)
{/*stuff*/}

Ancak isteğim için her zaman bir yanıt istemiyorum ve bir yanıt almak için her zaman istek verilerini beslemek istemiyorum. Ayrıca küçük değişiklikler yapmak için yöntemleri bütünüyle kopyalayıp yapıştırmak zorunda kalmak istemiyorum. İstediğim, bunu yapabilmek:

public Tre DoSomething<Tres>(Tres response)
{
    return DoSomething<Tres, void>(response, null);
}

Bu bir şekilde uygulanabilir mi? Görünüşe göre özellikle void kullanmak işe yaramıyor, ancak benzer bir şey bulmayı umuyorum.


1
Neden sadece System.Object kullanmıyorsunuz ve DoSomething'de boş bir kontrol (Tres yanıtı, Treq isteği) yapmıyorsunuz?
James

Dönüş değerini kullanmanız gerektiğini unutmayın. Prosedürler gibi işlevleri çağırabilirsiniz. DoSomething(x);yeriney = DoSomething(x);
Olivier Jacot-Descombes

1
Sanırım, " Dönüş değerini kullanmanıza gerek olmadığını unutmayın." Demek istediniz . @ OlivierJacot-Descombes
zanedp

Yanıtlar:


101

Kullanamazsınız void, ancak kullanabilirsiniz object: bu biraz rahatsızlıktır çünkü olası voidişlevlerinizin geri dönmesi gerekir null, ancak kodunuzu birleştirirse, ödenmesi gereken küçük bir bedel olmalıdır.

voidBir iade türü olarak kullanılamaması , jenerik delegelerin Func<...>ve Action<...>aileleri arasındaki bölünmeden en azından kısmen sorumludur : geri dönmek mümkün olsaydı void, hepsi Action<X,Y,Z>basitleşirdi Func<X,Y,Z,void>. Maalesef bu mümkün değil.


48
(Şaka) Ve yine voidde bu olasılık dışı yöntemlerden dönebilir return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(void));. Yine de kutulu bir boşluk olacak.
Jeppe Stig Nielsen

1
C # daha işlevsel programlama özelliklerini desteklediğinden , FP'yi temsil eden Birime bir göz atabilirsiniz void. Ve onu kullanmak için iyi sebepler var. F # 'de, hala .NET, biz unityerleşik var .
joe,

89

Hayır, ne yazık ki hayır. void"Gerçek" bir tip olsaydı (örneğin unitF # 'da olduğu gibi) hayat birçok yönden çok daha basit olurdu. Özellikle, hem gerek olmazdı Func<T>ve Action<T>sadece orada olurdu - ailelerini Func<void>yerine Action, Func<T, void>yerine Action<T>vs.

Aynı zamanda eşzamansızlığı daha basit hale getirirdi - jenerik olmayan türe hiç gerek kalmazdı Task- bizde olurduk Task<void>.

Ne yazık ki, C # veya .NET tipi sistemlerin çalışma şekli bu değil ...


4
Unfortunately, that's not the way the C# or .NET type systems work...Eninde sonunda her şeyin böyle yürümesi konusunda beni umutlandırıyordun. Son noktanız, işlerin bu şekilde yürümesi ihtimalimizin olmadığı anlamına mı geliyor?
Dave Cousineau

3
@Sahuagin: Sanmıyorum - bu noktada oldukça büyük bir değişiklik olur.
Jon Skeet

1
@stannius: Hayır, bence yanlış koda yol açan bir şeyden çok rahatsızlık verici.
Jon Skeet

2
Birim referans türünün "geçersiz" i temsil etmek için boş değer türüne göre avantajı var mı? Boş değer türü bana daha uygun görünüyor, değer taşımıyor ve yer kaplamıyor. Neden void'in böyle uygulanmadığını merak ediyorum. Yığından patlatmak veya patlatmamak hiçbir fark yaratmaz (yerel kod konuşmak, IL'de farklı olabilir).
Ondrej Petrzilka

1
@Ondrej: Daha önce şeyler için boş bir yapı kullanmayı denedim ve CLR'de boş kalmıyor ... Tabii ki özel kasalı olabilir. Bunun ötesinde, hangisini önereceğimi bilmiyorum; Bunun hakkında pek düşünmedim.
Jon Skeet

27

İşte yapabilecekleriniz. @JohnSkeet'in dediği gibi C # 'da birim türü yoktur, bu yüzden kendiniz yapın!

public sealed class ThankYou {
   private ThankYou() { }
   private readonly static ThankYou bye = new ThankYou();
   public static ThankYou Bye { get { return bye; } }
}

Artık Func<..., ThankYou>bunun yerine her zaman kullanabilirsinizAction<...>

public ThankYou MethodWithNoResult() {
   /* do things */
   return ThankYou.Bye;
}

Veya Rx ekibi tarafından önceden yapılmış bir şeyi kullanın: http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx


System.Reactive.Unit iyi bir öneridir. Kasım 2016 itibarıyla, Reactive çerçevesinden olabildiğince küçük bir parça almakla ilgileniyorsanız, Unit sınıfından başka bir şey kullanmadığınız için nuget paket yöneticisini kullanınInstall-Package System.Reactive.Core
DannyMeister

6
ThankYou'yu "KThx" olarak yeniden adlandırın ve kazanan olur. ^ _ ^Kthx.Bye;
LexH

Sadece bir şeyi kaçırmadığımı kontrol etmek için .. Hoşçakal alıcı buraya doğrudan erişim yerine önemli bir şey eklemiyor, değil mi?
Andrew

1
@Andrew, çıplak alanları açığa çıkarmamanız gerektiğini söyleyen C # kodlamasının ruhunu takip etmek istemiyorsanız, güle güle alıcıya ihtiyacınız yok
Trident D'Gao

17

ObjectBaşkalarının önerdiği gibi kullanabilirsiniz . Ya Int32da bazı kullanım gördüğüm. Kullanmak Int32bir "sahte" sayı (kullanım 0) getirir, ancak en azından herhangi bir büyük ve egzotik nesneyi bir Int32referansa koyamazsınız (yapılar mühürlenmiştir).

Kendi "void" türünüzü de yazabilirsiniz:

public sealed class MyVoid
{
  MyVoid()
  {
    throw new InvalidOperationException("Don't instantiate MyVoid.");
  }
}

MyVoidreferanslara izin verilir (bu statik bir sınıf değildir) ancak yalnızca olabilir null. Örnek kurucu özeldir (ve birisi bu özel kurucuyu yansıtma yoluyla çağırmaya çalışırsa, onlara bir istisna atılır).


Değeri demetler tanıtıldı yana (2017, .NET 4.7), kullanımı belki doğal yapıValueTuple yerine, örneğin a (0-tuple, genel olmayan varyant) MyVoid. Örneğinde ToString()dönen bir o vardır "()", bu nedenle sıfır demeti gibi görünür. Mevcut C # sürümünden itibaren, ()bir örnek almak için koddaki belirteçleri kullanamazsınız . Bunun yerine default(ValueTuple)veya just default(tür bağlamdan çıkarıldığında) kullanabilirsiniz.


2
Bunun başka bir adı boş nesne kalıbı (tasarım kalıbı) olacaktır.
Aelphaeis

@Aelphaeis Bu kavram, boş nesne modelinden biraz farklıdır. Burada önemli olan, jenerikle kullanabileceğimiz bir türe sahip olmaktır. Boş nesne deseninin amacı, özel bir durumu belirtmek için boş döndüren ve bunun yerine uygun varsayılan davranışa sahip gerçek bir nesneyi döndüren bir yöntem yazmaktan kaçınmaktır.
Andrew Palmer

8

Aleksey Bykov'un yukarıdaki fikrini beğendim, ancak biraz basitleştirilebilir

public sealed class Nothing {
    public static Nothing AtAll { get { return null; } }
}

Nothing.AtAll neden hiçbir açık neden görmediğim için boş veremez

Aynı fikir (veya Jeppe Stig Nielsen'in fikri), tiplenmiş sınıflarla kullanım için de harika.

Örneğin, tür yalnızca bir yönteme / yönteme bağımsız değişken olarak iletilen bir yordam / işleve ilişkin bağımsız değişkenleri tanımlamak için kullanılıyorsa ve kendisi herhangi bir bağımsız değişken almazsa.

(Yine de ya sahte bir sarmalayıcı yapmanız ya da isteğe bağlı bir "Hiçbir Şey" e izin vermeniz gerekecek. Ancak IMHO, sınıfın kullanımı myClass <Nothing> ile güzel görünüyor)

void myProcWithNoArguments(Nothing Dummy){
     myProcWithNoArguments(){
}

veya

void myProcWithNoArguments(Nothing Dummy=null){
    ...
}

1
Değer null, eksik veya eksik olma çağrışımına sahiptir object. Bir için tek bir değere sahip olmak Nothing, asla başka bir şeye benzemediği anlamına gelir.
LexH

Bu iyi bir fikir ama @LexieHankins'e katılıyorum. "Hiçbir şeyin" Nothingözel bir statik alanda depolanan ve özel bir kurucu ekleyen sınıfın benzersiz bir örneği olması daha iyi olur . Boş hala mümkün olması can sıkıcı, ama bu umutla C # 8 ile, çözülecek
Kirk Woll

Akademik olarak ayrımı anlıyorum, ancak ayrımın önemli olduğu çok özel bir durum olurdu. ("Null" değerinin dönüşünün özel bir işaretçi olarak kullanıldığı, örneğin bir tür hata işaretçisi olarak kullanıldığı, genel null atanabilir tipte bir işlev düşünün)
Eske Rahn

4

voidbir tür olsa da, yalnızca bir yöntemin dönüş türü olarak geçerlidir.

Bu sınırlamayı aşmanın yolu yok void.


1

Şu anda yaptığım şey, özel oluşturucu ile özel mühürlü türler oluşturmaktır. Bu, c-tor'a istisnalar atmaktan daha iyidir çünkü durumun yanlış olduğunu anlamak için çalışma zamanına kadar beklemeniz gerekmez. Statik bir örnek döndürmekten ince bir şekilde daha iyidir çünkü bir kez bile ayırmanız gerekmez. Çağrı tarafında daha az ayrıntılı olduğundan statik boş döndürmekten biraz daha iyidir. Arayanın yapabileceği tek şey boş vermektir.

public sealed class Void {
    private Void() { }
}

public sealed class None {
    private None() { }
}
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.