void in C# generics?
C#GenericsC# Problem Overview
I have a generic method that takes a request and provides a response.
public Tres DoSomething<Tres, Treq>(Tres response, Treq request)
{/*stuff*/}
But I don't always want a response for my request, and I don't always want to feed request data to get a response. I also don't want to have to copy and paste methods in their entirety to make minor changes. What I want, is to be able to do this:
public Tre DoSomething<Tres>(Tres response)
{
return DoSomething<Tres, void>(response, null);
}
Is this feasible in some manner? It seems that specifically using void doesn't work, but I'm hoping to find something analogous.
C# Solutions
Solution 1 - C#
You cannot use void
, but you can use object
: it is a little inconvenience because your would-be-void
functions need to return null
, but if it unifies your code, it should be a small price to pay.
This inability to use void
as a return type is at least partially responsible for a split between the Func<...>
and Action<...>
families of generic delegates: had it been possible to return void
, all Action<X,Y,Z>
would become simply Func<X,Y,Z,void>
. Unfortunately, this is not possible.
Solution 2 - C#
No, unfortunately not. If void
were a "real" type (like unit
in F#, for example) life would be a lot simpler in many ways. In particular, we wouldn't need both the Func<T>
and Action<T>
families - there'd just be Func<void>
instead of Action
, Func<T, void>
instead of Action<T>
etc.
It would also make async simpler - there'd be no need for the non-generic Task
type at all - we'd just have Task<void>
.
Unfortunately, that's not the way the C# or .NET type systems work...
Solution 3 - C#
Here is what you can do. As @JohnSkeet said there is no unit type in C#, so make it yourself!
public sealed class ThankYou {
private ThankYou() { }
private readonly static ThankYou bye = new ThankYou();
public static ThankYou Bye { get { return bye; } }
}
Now you can always use Func<..., ThankYou>
instead of Action<...>
public ThankYou MethodWithNoResult() {
/* do things */
return ThankYou.Bye;
}
Or use something already made by the Rx team: http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx
Solution 4 - C#
You could simply use Object
as others have suggested. Or Int32
which I have seen some use. Using Int32
introduces a "dummy" number (use 0
), but at least you can't put any big and exotic object into an Int32
reference (structs are sealed).
You could also write you own "void" type:
public sealed class MyVoid
{
MyVoid()
{
throw new InvalidOperationException("Don't instantiate MyVoid.");
}
}
MyVoid
references are allowed (it's not a static class) but can only be null
. The instance constructor is private (and if someone tries to call this private constructor through reflection, an exception will be thrown at them).
Since value tuples were introduced (2017, .NET 4.7), it is maybe natural to use the struct ValueTuple
(the 0-tuple, the non-generic variant) instead of such a MyVoid
. Its instance has a ToString()
that returns "()"
, so it looks like a zero-tuple. As of the current version of C#, you cannot use the tokens ()
in code to get an instance. You can use default(ValueTuple)
or just default
(when the type can be inferred from the context) instead.
Solution 5 - C#
I like the idea by Aleksey Bykov above, but it could be simplified a bit
public sealed class Nothing {
public static Nothing AtAll { get { return null; } }
}
As I see no apparent reason why Nothing.AtAll could not just give null
The same idea (or the one by Jeppe Stig Nielsen) is also great for usage with typed classes.
E.g. if the type is only used to describe the arguments to a procedure/function passed as an argument to some method, and it itself does not take any arguments.
(You will still need to either make a dummy wrapper or to allow an optional "Nothing". But IMHO the class usage looks nice with myClass<Nothing> )
void myProcWithNoArguments(Nothing Dummy){
myProcWithNoArguments(){
}
or
void myProcWithNoArguments(Nothing Dummy=null){
...
}
Solution 6 - C#
void
, though a type, is only valid as a return type of a method.
There is no way around this limitation of void
.
Solution 7 - C#
What I currently do is create custom sealed types with private constructor. This is better than throwing exceptions in the c-tor because you don't have to get until runtime to figure out the situation is incorrect. It is subtly better than returning a static instance because you don't have to allocate even once. It is subtly better than returning static null because it is less verbose on the call side. The only thing the caller can do is give null.
public sealed class Void {
private Void() { }
}
public sealed class None {
private None() { }
}