Reputation: 319
I know this question has been asked multiple times before, but I've checked all I could not find any solution there, because none of this specifically handles/talks about nullable types.
I want to generate test data dynamically. To do this the value
I pass in can have different types and I need to convert it (via CreateSomething
) in some data type e.g. a specially formatted string. For this, I have the helper function shown below.
An example would be to generate an example XML file for later use in testing against the system in production. The value
is then used to assert the correct expected output.
Note that for some types I need special handling, such as converting bools to integers (as you can see below), that's why I need a switch. In addition, the egnerated string mentions the type of the test data (though it does not match all types you have in C#/uses other names, e.g. booleans are defined as integers).
With C# 8.0 this is possible:
public static string GetSomething(object? value) =>
value switch
{
bool boolValue => CreateSomething("Integer", boolValue
? 1
: 0),
string stringValue => CreateSomething("String", stringValue),
int numericValue => CreateSomething("String", numericValue.ToString()),
null => CreateSomething("String", value),
_ => throw new ArgumentOutOfRangeException(nameof(value))
};
CreateSomething
is defined as this:
string CreateSomething(string customType, object? value)
However, as for the null
case (maked above), I further want to handle the type.
As you can see with the other cases, I want to pass Integer
in there for all bool
values as the first parameter, and String
for all others.
Of course, the value
is and stays null
, but that is about to be expected, I just want to keep the information/pass the information on about what type it should expect/define the thing in my test data, because otherwise it is defined as another data type and thus does not produce as real test data as it could (without disregarding the type).
I thought of using a second switch statement in there/or use .GetType
, but of course that doe snot work as .GetType
on null
throws a NullReferenceException
.
I also looked at getting the underlying type of a nullable via Nullable.GetUnderlyingType(value)
, but that did not work either. And this question to get the type from null
only has answers using Generics, which I don't want in my case.
After all, I do have this nice C# switch type pattern matching statement I can use for all types - why can't I also use it for nullable types?
After all, this would be ideal:
/* Code does not work! Only demonstration! */
public static string GetSomething(object? value) =>
value switch
{
bool? boolValue => CreateSomething("Integer", boolValue == null
? null : (boolValue ? 1 : 0),
string? stringValue => CreateSomething("String", stringValue),
int? numericValue => CreateSomething("String", numericValue?.ToString()),
_ => throw new ArgumentOutOfRangeException(nameof(value))
};
Of course, I tried that and it also did not work.
It just shows me the error CS8400 ("Feature type pattern is not available in C# 8.0. Please use language version 9.0 or higher.") and "Type test pattern requires variable designation or discard after.". And even if I use C# 9 it breaks the syntax while not showing me any explicit compiler error anymore.
Upvotes: 0
Views: 1828
Reputation: 6806
The problem is that you are trying to switch on nullable types. That won't work. Suppose someone called your function like this:
GetSomething(null);
What should the GetSomething
function treat that as? A nullable bool? A nullable int? There's no way to tell.
You need to check for null first and then treat the other possibilities as normal types. Maybe like this?
public static string GetSomething(object? value)
{
if (value == null)
return "null";
return value switch
{
bool boolValue => CreateSomething("Integer", boolValue ? 1 : 0),
string stringValue => CreateSomething("String", stringValue),
int numericValue => CreateSomething("String", numericValue.ToString()),
null => CreateSomething("String", value),
_ => throw new ArgumentOutOfRangeException(nameof(value))
};
}
If you really need something different for a nulled bool vs a nulled int, then you will need to also pass in the datatype that you were expecting to read. Like this
public static string GetSomething(object? value, Type expectedType)
Upvotes: 1