How to write generic method for casting DBNull value to Nullable? - c#

I have a database with a nullable column foo_date, where Npgsql maps the sql NULL value to an instance of the C# class DBNull. In my C# aggregate I use the type DateTime? for said column. So the question is how to easily convert DBNull to a nullable type.
I want to write a utility method like, e.g.,
public static class DbUtil
{
public static T? CastToNullable<T>(object obj)
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}
}
which I would like to use like this:
IDataRecord rec = ...
DateTime? fooDate = DbUtil.CastToNullable<DateTime>(rec["foo_date"]);
However, I get the compiler error:
Error CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
When I replace return null by return default(T?), the compiler is happy, but the method does not return null but the default Date, i.e., 01.01.0001.
What is the correct way to write the generic utility method above?

I'm assuming you've got nullable reference types enabled, otherwise that T? would be a compiler error.
T?, where T is a generic type parameter, has multiple meanings unfortunately.
When T is a value type (i.e. you have a where T : struct constraint), T? means Nullable<T>.
When T is a reference type (i.e. you have a where T : class constraint), T? means a reference type which is allowed to be null.
When T is unconstrained, T? means "If T is a reference type, then this is allowed to be null; otherwise no effect".
In other words, if you have:
T? Foo<T>() => default;
If you call Foo<int>(), you get back an int, not an int?.
If however you have:
T? Foo<T>() where T : struct => default;
then Foo<int>() returns an int?.
In other words, your signature needs to be:
public static T? CastToNullable<T>(object obj) where T : struct
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}

Please check Nullable structure which is in fact long/real version of T?
In that case your method would be
public static T? CastToNullable<T>(object obj) where T: struct
{
if (DBNull.Value.Equals(obj))
return null;
return new Nullable<T>((T)obj);
}
That would work only for structures, for reference types you could add
public static T CastToNullableObj<T>(object obj) where T: class
{
if (DBNull.Value.Equals(obj))
return null;
return (T)obj;
}

It seems like you want to access columns on your NpgsqlDataReader, but to get .NET null for null columns instead of DBNull.Value. If so, the usual way is to write use an extension method as follows:
public static class DbDataReaderExtensions
{
public static T? GetValueOrDefault<T>(this DbDataReader reader, int ordinal)
where T : class
=> reader.IsDBNull(ordinal) ? null : reader.GetFieldValue<T>(ordinal);
}
This can be used directly on your reader:
var s = reader.GetValueOrDefault<string>(0);

Related

C#: default of generic T? is not null; behavior changes with generic constraint

I have a generic class that should operate on (non-nullable) reference and value types (parameters, returns ...) but internally needs fields that can be null.
using System;
public class Gen<T> // where T : struct
{
public class Data
{
public T? t;
}
public static void Write(string s)
{
Data d = new Data();
Console.WriteLine("Default of {0} is {1}", s, d.t == null ? "null" : "NOT null");
}
// ... other stuff that uses T and not T? like
// public T DoSomething(T value) ...
}
static class Program
{
static void Main(string[] args)
{
Gen<int>.Write("int?");
Gen<string>.Write("string?");
}
}
This code does not produce any errors or warnings when compiled (.NET 5) with nullable enabled.
However the behavior is not as I have expected.
Default of int? is NOT null
Default of string? is null
While playing around searching for a solution, I discovered that when the where T : struct constraint is added (and Gen.Write() removed), the behavior changes to
Default of int? is null
It's odd that a constraint changes the behavior.
Does anybody know a elegant solution to write such a generic class?
Using a custom Nullable class that supports reference types too or a separate bool flags for every T? filed is a bit tedious.
If you want to use Nullable<int> you shouldn't use int, so use:
Gen<int?>.Write("int?");
Then the output will be
Default of int? is null
Default of string? is null
The code in the question is an example. The real class does not have a
Write method and never uses the string of the type. However as I
indicated by 'other stuff' it uses T as well as T?. So it is not
desired to instantiate it with int? instead of int.
First i want to explain why it's not odd that the struct constraint in the generic class changes the behavior. Because actually that constraint makes it compile if you are < C#8. Then T? means Nullable<T>, so if you use Gen<int>.Write("int?") the field t will be a Nullable<int>. But then Gen<string>.Write("string") won't compile at all since string is not a struct. So it has a completely different meaning with the constraint.
With C#8 enabled you can remove the struct constrained, then t remains an int and the string will be a nullable string. So the question mark has the meaning: in case of a reference type it's a nullable reference type, otherwise it's just what it is.
You can't have both, a generic type that can be a nullable reference type or a nullable value type without using the desired generic type, so use int? if it must be nullable.

Receiving error about nullable type parameter even when parameter has notnull constraint

I have a generic interface IDataAdapter<T>; implementors of the interface should be able to read POCOs with Guid IDs from a data source. IDataAdapter<T> has a method Read(Guid id) which I want to return a T?, where null indicates that no matches were found in the data source. However, even with a constraint T : notnull on IDataAdapter<T>, attempting to define this method gives the error CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint. Why am I still getting this error, even with T constrained to notnull?
Code (should be in a C# 8 environment with <Nullable>enable</Nullable>):
interface IDataAdapter<T> where T : notnull
{
T? Read (Guid id); // error CS8627
}
I think this issue is very similar to what is happening in this post.
Note that a T? where T : class and a T? where T : struct are represented very differently in the CLR. The former is just the CLR type T. There are not separate types in the CLR to differentiate between T and T?. T? in C# just adds extra compile time checking by the C# compiler. On the other hand, The latter is represented by the CLR type Nullable<T>.
So let's consider your method:
T? Read (Guid id);
How should this be represented in the CLR? What is the return type? The compiler don't know whether T is a reference type or a value type, so the compiler cannot decide whether the method signature should be:
T Read (Guid id);
or:
Nullable<T> Read (Guid id);
The same error is raised if you don't use the notnull constraint. You need to specify what that type is with a class or struct constraint. You don't need to specify notnull as structs were always nullable and, with nullable ref types enabled, so are classes.
Just add where T:class or where T:struct.
Reference Types
If you add the class constraint, eg:
#nullable enable
interface IDataAdapter<T>
where T:class
{
T? Read (Guid id); // error CS8627
void Something(T input);
}
class StringAdapter:IDataAdapter<string>
{
public string Read(Guid id)=>id.ToString();
public void Something(string input){}
}
The following call will generate a warning:
var adp=new StringAdapter();
string? x=null;
adp.Something(x); //CS8604: Possible null reference argument ....
Value Types
Using struct to create an IntAdapter on the other hand results in a compilation error if the argument is nullable :
interface IDataAdapter<T>
where T:struct
{
T? Read (Guid id); // error CS8627
void Something(T input);
}
class IntAdapter:IDataAdapter<int>
{
public int? Read(Guid id)=>id.ToString().Length;
public void Something(int input){}
}
void Main()
{
var adp=new IntAdapter();
int? x=null;
adp.Something(x); //CS1503: Cannot convert from int? to int
}
That's because the compile generated methods that expect an int? instead of an int.
Explanation
The reason is that the compiler has to generate very different code in each case. For a class, it doesn't have to do anything special. For a struct, it has to generate a Nullable< T>.
This is explained in the The issue with T? section in Try out Nullable Reference Types :
This distinction between nullable value types and nullable reference types comes up in a pattern such as this:
void M<T>(T? t) where T: notnull
This would mean that the parameter is the nullable version of T, and T is constrained to be notnull. If T were a string, then the actual signature of M would be M([NullableAttribute] T t), but if T were an int, then M would be M(Nullable t). These two signatures are fundamentally different, and this difference is not reconcilable.
Because of this issue between the concrete representations of nullable reference types and nullable value types, any use of T? must also require you to constrain the T to be either class or struct.
If you look at Nullable Struct's documentation you can see that it requires to be a struct:
public struct Nullable<T> where T : struct
I believe you will need to constraint T to be a struct:
interface IA<T> where T : struct
{
T? Read(Guid id);
// Or Nullable<T> Read(Guid id);
}
class A : IA<int>
{
public int? Read(Guid id) { Console.WriteLine("A"); return 0; }
}
BTW. Could you give us an example of what types you want to use this class with?
Why not just use where T: class and return T (or even not have a constraint at all)?
interface IA<T> where T : class
{
T Read(Guid id);
}

Nullable reference types with generic return type

I'm playing around a bit with the new C# 8 nullable reference types feature, and while refactoring my code I came upon this (simplified) method:
public T Get<T>(string key)
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : default;
}
Now, this gives a warning
Possible null reference return
which is logical, since default(T) will give null for all reference types. At first I thought I would change it to the following:
public T? Get<T>(string key)
But this cannot be done. It says I either have to add a generic constraint where T : class or where T : struct. But that is not an option, as it can be both (I can store an int or int? or an instance of FooBar or whatever in the cache).
I also read about a supposed new generic constraint where class? but that did not seem to work.
The only simple solution I can think of is changing the return statement using a null forgiving operator:
return wrapper.HasValue ? Deserialize<T>(wrapper) : default!;
But that feels wrong, since it can definitely be null, so I'm basically lying to the compiler here..
How can I fix this? Am I missing something utterly obvious here?
You were very close. Just write your method like this:
[return: MaybeNull]
public T Get<T>(string key)
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : default!;
}
You have to use the default! to get rid of the warning. But you can tell the compiler with [return: MaybeNull] that it should check for null even if it's a non-nullable type.
In that case, the dev may get a warning (depends on flow analytics) if he uses your method and does not check for null.
For further info, see Microsoft documentation: Specify post-conditions: MaybeNull and NotNull
I think default! is the best you can do at this point.
The reason why public T? Get<T>(string key) doesn't work is because nullable reference types are very different from nullable value types.
Nullable reference types is purely a compile time thing. The little question marks and exclamation marks are only used by the compiler to check for possible nulls. To the eyes of the runtime, string? and string are exactly the same.
Nullable value types on the other hand, is syntactic sugar for Nullable<T>. When the compiler compiles your method, it needs to decide the return type of your method. If T is a reference type, your method would have return type T. If T is a value type, your method would have a return type of Nullable<T>. But the compiler don't know how to handle it when T can be both. It certainly can't say "the return type is T if T is a reference type, and it is Nullable<T> if T is a reference type." because the CLR wouldn't understand that. A method is supposed to only have one return type.
In other words, by saying that you want to return T? is like saying you want to return T when T is a reference type, and return Nullable<T> when T is a value type. That doesn't sound like a valid return type for a method, does it?
As a really bad workaround, you could declare two methods with different names - one has T constrained to value types, and the other has T constrained to reference types:
public T? Get<T>(string key) where T : class
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : null;
}
public T? GetStruct<T>(string key) where T : struct
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? (T?)Deserialize<T>(wrapper) : null;
}
In C# 9 you are able to express nullability of unconstrained generics more naturally:
public T? Get<T>(string key)
{
var wrapper = cacheService.Get(key);
return wrapper.HasValue ? Deserialize<T>(wrapper) : default;
}
Note there's no ! operator on the default expression. The only change from your original example is the addition of ? to the T return type.
In addition to Drew's answer about C# 9
Having T? Get<T>(string key) we still need to distinguish nullable ref types and nullable value types in the calling code:
SomeClass? c = Get<SomeClass?>("key"); // return type is SomeClass?
SomeClass? c2 = Get<SomeClass>("key"); // return type is SomeClass?
int? i = Get<int?>("key"); // return type is int?
int i2 = Get<int>("key"); // return type is int

Extension method for nullable enum

I'm trying to write an Extension method for nullable Enums.
Like with this example:
// ItemType is an enum
ItemType? item;
...
item.GetDescription();
So I wrote this method which doesn't compile for some reason that I don't understand:
public static string GetDescription(this Enum? theEnum)
{
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
I'm getting the following error on Enum?:
only non-nullable value type could be underlying of system.nullable
Why? Enum can not have the value null!
Update:
If have lots of enums, ItemType is just an example of one of them.
System.Enum is a class, so just drop the ? and this should work.
(By "this should work", I mean if you pass in a null-valued ItemType?, you'll get a null Enum in the method.)
public static string GetDescription(this Enum theEnum)
{
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
enum Test { blah }
Test? q = null;
q.GetDescription(); // => theEnum parameter is null
q = Test.blah;
q.GetDescription(); // => theEnum parameter is Test.blah
You can simply make your extension method generic:
public static string GetDescription<T>(this T? theEnum) where T : struct
{
if (!typeof(T).IsEnum)
throw new Exception("Must be an enum.");
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
Unfortunately, you cannot use System.Enum in a generic constraint, so the extension method will show for all nullable values (hence the extra check).
EDIT: C# 7.3 introduced new generic constraints which now allow restricting a generic argument to an enum, like so:
public static string GetDescription<T>(this T? theEnum) where T : Enum
{
if (theEnum == null)
return string.Empty;
return GetDescriptionAttribute(theEnum);
}
Thanks #JeppeStigNielsen for pointing that out.
You should use the actual enum type in your method signature:
public static string GetDescription(this ItemType? theEnum)
System.ValueType and System.Enum are not treated as value types (only types derived from them), so they are nullable (and you don't to specify them as nullable). Try it:
// No errors!
ValueType v = null;
Enum e = null;
You could also try this signature:
public static string GetDescription<T>(this T? theEnum) where T: struct
This also allows structs though, which might not be what you want. I think i remember some library that adds a type constraint of enum after compilation (C# doesn't allow it) though. Just need to find it...
EDIT: Found it:
http://code.google.com/p/unconstrained-melody/
Maybe better is add extra value to your enum and call it null :)

Return null from generic method

How do I return null value from a generic method?
protected T ValueOrDefault<T>(IDataReader reader, int ordinalId)
{
Type t = typeof(reader.GetValue(ordinalId));
if (t.IsValueType){
//Struct. How do I return null?
} else {
//Class
//just return null
return default(T);
}
}
default(T) works in both cases.
default(T) does function in both cases, but its meaning is slightly different for value types. It literally returns the default value of the type. In the case of Method<int>, it will return 0, not null.
UPDATE: Given your method signature:
protected T ValueOrDefault<T>(IDataReader reader, int ordinalId)
You can't return Nullable<T> in the case of a value type and type T in the case of a reference type. That would have to be two different methods.
Obviously you can only return null if the return type is either Nullable<T> or a reference type. Normal value-types have no null.
For reference types default(T) is null, and for Nullable<T> it's null too. So you can use default(T) in both cases where null exists.
If the type is another value-type default(T) will not be null, but since there is no null that wouldn't make sense anyways.
It is not possible to simply have a method that has return type T if T is a reference-type/Nullable<T> and T? for normal value types.
One could try to define something like this, but it won't compile because the compiler doesn't understand that the generic constraints are mutually exclusive. It just doesn't consider generic constraints for that.
T? a<T>()
where T:struct
{
}
T a<T>()
where T:class
{
}
You need to make these methods different in some other way. Either by using different names or different parameters.
If you want to return T? if it's a value type, you have to use two separate methods. However, there is a complexity, in that methods can't differ only by their return type (there's also issues around the generic args not being part of a method signature). So you have to provide a 'stub' method parameter which the compiler uses to resolve which method to call:
public T MyMethod<T>(T stub) where T : class {
// ...
return null;
}
public T? MyMethod<T>(T? stub) where T : struct {
// ...
return null;
}
// this will then compile...
string s = MyMethod<string>(null);
int? i = MyMethod<int>(null);
It's not legal to return null for an unconstrained T value. Consider for instance if T is instantiated as a value type. In that case null would not be a legal value and hence the code is illegal.
What you're looking for is default(T). This will work for both value and reference types. For reference types it will produce null and for value types it will produce the zero initialized value.
In C# 9, it is possible to have a single method return null for reference and for nullable value types. Note that string and string? are exactly the same type but int and int? are different (Int32 and Nullable).
[TestMethod]
public void TestGenericNull()
{
static T? GetDefault<T>(bool wantNullable) => wantNullable ? default(T?) : default(T);
string? s1 = GetDefault<string>(false);
Assert.IsNull(s1);
string? s2 = GetDefault<string>(true);
Assert.IsNull(s2);
int? d1 = default(int);
Assert.AreEqual(d1, 0);
int? d2 = default(int?);
Assert.IsNull(d2);
int? n1 = GetDefault<int>(false);
Assert.AreEqual(n1, 0);
int? n2 = GetDefault<int>(true);
Assert.AreEqual(n2, 0); // Expected default(int?), i.e. Nullable<int> with no value.
int? n4 = GetDefault<int?>(false);
Assert.IsNull(n4);
int? n5 = GetDefault<int?>(true);
Assert.IsNull(n5);
}
To see why this isn't possible, try replacing T with a value type:
protected int ValueOrDefault<int>(IDataReader reader, int ordinalId)
{
Type t = typeof(reader.GetValue(ordinalId));
if (t.IsValueType){
//Struct. How do I return null?
} else {
//Class
//just return null
return default(int);
}
}
If the return type is int, you can't return null because it's not a valid value. The type of T would have to be nullable in the first place, in which case default(T) works.
You can use just return default without (T) at the end. C# 7.1 or above.

Categories