Mam metodę ogólną, która przyjmuje żądanie i zapewnia odpowiedź.
public Tres DoSomething<Tres, Treq>(Tres response, Treq request)
{/*stuff*/}
Ale nie zawsze chcę odpowiedzi na moje żądanie i nie zawsze chcę podawać dane żądania, aby uzyskać odpowiedź. Nie chcę też kopiować i wklejać metod w całości, aby wprowadzić drobne zmiany. To, czego chcę, to móc to zrobić:
public Tre DoSomething<Tres>(Tres response)
{
return DoSomething<Tres, void>(response, null);
}
Czy jest to w jakiś sposób wykonalne? Wygląda na to, że użycie void nie działa, ale mam nadzieję, że znajdę coś analogicznego.
DoSomething(x);
zamiasty = DoSomething(x);
Odpowiedzi:
Nie możesz użyć
void
, ale możesz użyćobject
: jest to trochę niewygodne, ponieważ Twoje przyszłevoid
funkcje muszą zostać zwróconenull
, ale jeśli ujednolici Twój kod, powinna to być niewielka cena.Ta niezdolność do wykorzystania
void
jako typ zwracany jest przynajmniej częściowo odpowiedzialny za rozłam pomiędzyFunc<...>
iAction<...>
rodzin delegatów generycznych: gdyby było to możliwe, aby wrócićvoid
, wszystkoAction<X,Y,Z>
stanie się po prostuFunc<X,Y,Z,void>
. Niestety nie jest to możliwe.źródło
void
z tych niedoszłych metodreturn System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(void));
. Będzie to jednak pustka w pudełku.void
w FP. I są dobre powody, by go używać. W języku F #, nadal .NET, mamyunit
wbudowany.Nie, niestety nie. Gdyby
void
był to typ „prawdziwy” (jakunit
na przykład F # ), życie byłoby o wiele prostsze na wiele sposobów. W szczególności nie potrzebowalibyśmy zarówno rodziny, jakFunc<T>
iAction<T>
- po prostu byłobyFunc<void>
zamiastAction
,Func<T, void>
zamiastAction<T>
itp.Ułatwiłoby to również asynchronizację - w ogóle nie byłoby potrzeby stosowania
Task
typu nieogólnego - po prostu byśmy to zrobiliTask<void>
.Niestety tak nie działa system typu C # czy .NET ...
źródło
Unfortunately, that's not the way the C# or .NET type systems work...
Sprawiałeś, że miałem nadzieję, że może w końcu tak się wszystko potoczy. Czy twoja ostatnia uwaga oznacza, że prawdopodobnie nigdy nie będziemy mieć takiego rozwiązania?Oto, co możesz zrobić. Jak powiedział @JohnSkeet, w C # nie ma typu jednostki, więc zrób to sam!
public sealed class ThankYou { private ThankYou() { } private readonly static ThankYou bye = new ThankYou(); public static ThankYou Bye { get { return bye; } } }
Teraz możesz zawsze użyć
Func<..., ThankYou>
zamiastAction<...>
public ThankYou MethodWithNoResult() { /* do things */ return ThankYou.Bye; }
Lub użyj czegoś już stworzonego przez zespół Rx: http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx
źródło
Install-Package System.Reactive.Core
Kthx.Bye;
Możesz po prostu użyć
Object
tego, co sugerowali inni. LubInt32
które widziałem w pewnym sensie. UżycieInt32
wprowadza "fikcyjną" liczbę (użyj0
), ale przynajmniej nie możesz umieścić żadnego dużego i egzotycznego obiektu wInt32
referencji (struktury są zapieczętowane).Możesz też napisać własny typ „void”:
public sealed class MyVoid { MyVoid() { throw new InvalidOperationException("Don't instantiate MyVoid."); } }
MyVoid
odniesienia są dozwolone (nie jest to klasa statyczna), ale mogą być tylkonull
. Konstruktor instancji jest prywatny (i jeśli ktoś spróbuje wywołać ten prywatny konstruktor przez odbicie, zostanie do niego zgłoszony wyjątek).Ponieważ wprowadzono krotki wartości (2017, .NET 4,7), może być naturalne użycie struktury
ValueTuple
(krotka 0, wariant nieogólny) zamiast takiegoMyVoid
. Jego wystąpienie maToString()
zwracaną wartość"()"
, więc wygląda jak zerowa krotka. W obecnej wersji C # nie można użyć tokenów()
w kodzie, aby uzyskać wystąpienie. Możesz zamiast tego użyćdefault(ValueTuple)
lub po prostudefault
(gdy typ można wywnioskować z kontekstu).źródło
Podoba mi się powyższy pomysł Aleksieja Bykowa, ale można go trochę uprościć
public sealed class Nothing { public static Nothing AtAll { get { return null; } } }
Ponieważ nie widzę wyraźnego powodu, dla którego Nothing.AtAll nie może po prostu dać null
Ten sam pomysł (lub ten autorstwa Jeppe Stiga Nielsena) jest również świetny do użycia z klasami typizowanymi.
Np. Jeśli typ jest używany tylko do opisu argumentów procedury / funkcji przekazanej jako argument do jakiejś metody, a sama nie przyjmuje żadnych argumentów.
(Nadal będziesz musiał albo utworzyć ślepą otokę, albo zezwolić na opcjonalne „Nic”. Ale IMHO użycie klasy wygląda ładnie z myClass <Nothing>)
void myProcWithNoArguments(Nothing Dummy){ myProcWithNoArguments(){ }
lub
void myProcWithNoArguments(Nothing Dummy=null){ ... }
źródło
null
ma konotację braku lub nieobecnościobject
. Posiadanie tylko jednej wartości aNothing
oznacza, że nigdy nie wygląda jak nic innego.Nothing
przechowywaną w prywatnym polu statycznym i dodającą prywatnego konstruktora. Fakt, że wartość null jest nadal możliwa, jest denerwujący, ale zostanie to rozwiązane, miejmy nadzieję, w C # 8.void
, choć typ, jest poprawny tylko jako typ zwracany metody.Nie ma sposobu na obejście tego ograniczenia
void
.źródło
Obecnie tworzę niestandardowe zapieczętowane typy za pomocą prywatnego konstruktora. Jest to lepsze niż rzucanie wyjątków w c-tor, ponieważ nie musisz uzyskiwać czasu do uruchomienia, aby dowiedzieć się, że sytuacja jest nieprawidłowa. Jest to subtelnie lepsze niż zwracanie instancji statycznej, ponieważ nie trzeba przydzielać ani razu. Jest to subtelnie lepsze niż zwracanie statycznej wartości null, ponieważ jest mniej rozwlekłe po stronie wywołania. Jedyne, co dzwoniący może zrobić, to podać wartość null.
public sealed class Void { private Void() { } } public sealed class None { private None() { } }
źródło