EDIT: see my update below on my stance on Null in C#8.0 before giving me options to patterns and such
Original question
I am trying to upgrade my base libraries to be "null enabled", aka using the C# 8.0 <Nullable>enable</Nullable> flag.
When trying to work with abstractions, and in specific generics, I ran into some problems. Consider the following code snippet which takes in an Action and converts this to a Func<TResult>. This is pre-nullable-enable:
public static Func<TResult> ToFunc<TResult>(this Action action)
=> () => { action(); return default; }
;
but post-nullable-enable I seem to struggle, since I cant make TResult nullable (or TResult?) as wit would require a constraint of either where TResult: class or where TResult: struct. There is no way I can combine these two constraints to let the compiler know that TResult can be a class or a valuetype. Which at the moment I find annoying - as one should be able to express it doesn't matter whether class or struct, it'll be nullable (regardless of previous .NET intherited design).
So, post-nullable-enable I seem to have only one option, and that is code duplication, examples below:
public static Func<TResult?> ToFuncClass<TResult>(this Action action)
where TResult : class
=> () => { action(); return null; }
;
public static Func<TResult?> ToFuncStruct<TResult>(this Action action)
where TResult : struct
=> () => { action(); return null; }
;
Both the code duplication as well as the naming schemes that come with it bother me a lot. I might be misunderstanding proper usage, or maybe I'm missing another feature of the spec, but how would you solve this?
UPDATE: In fact, I think I'd rather stick to my own "null-handling" implementations than using C#8.0's nullable feature. As a "Void" object or fleshed out "Option/Maybe/None"-solution seems to communicate things better. The only thing I am concerned about is that it isn't very helpful in moving the language forward, training new coders and introduces another third party/non native solutation to a universal provlem we all have dealing with null. Because own implementations of handling with null are great and all, but come up differently in each code base, need to be maintained by the community, and you have various different flavours. So it would be so helpful and hugely beneficial if the language enforced it fully, and if a standard rised. Which I hoped this would be - clearly it is not, and I understand. But I feel this is a missed opportunity.
Thx Yves