1

Dummy code:

// TSource1 elementObj can be struct or can be class object and never can be nullable struct or null (because method inner code will create Exceptions in that case)
public static TSource1? ReturnNullOrNotNullTSource1<TSource1>(TSource1 elementObj) 
{
    // making some checkings for elementObj, if chekings have passed I need to return that elementObj back, if chekings have not passed I need to return null (so I used TSource1? as a method return type).
    // ...
    return (passed) ? elementObj : default(TSource1?);
}

I can return default(TSource1?) with a case when checkings have not passed and that code is compiled, but when I test it with TSource1=DateOnly I see that the actual object that was returned is not null but '01.01.0001'. It returns default(TSource1) instead default(TSource1?) and it is incorrect for me because I can't know have checkings not passed or they have passed for this case: var result = ReturnNullOrNotNullTSource1(new DateOnly(0001, 1, 1));

I expect I can return nullable struct T for incoming non-nullable struct T but that code works wrong. What can I do?

0

1 Answer 1

3

You didn't specify the type parameter, so when you call it as ReturnNullOrNotNullTSource1(new DateOnly(0001, 1, 1));, the type parameter will be inferred as DateOnly.

But since the type parameter TSource1 is unconstrained, the meaning of TSource1? is not as you expected, you can find the rule in this link.

If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.

Thus default(TSource1?) is equivalent to default(DateOnly).

You can either specify the type parameter

ReturnNullOrNotNullTSource1<DateOnly?>(new DateOnly(0001, 1, 1));

Or add a struct constraint

public static TSource1? ReturnNullOrNotNullTSource1<TSource1>(TSource1 elementObj)
    where TSource1 : struct

Another approach is using an out parameter to return the nullable object

void ReturnNullOrNotNullTSource1<T>(T elementObj, out T? output) where T: struct
void ReturnNullOrNotNullTSource1<T>(T elementObj, out T? output) where T: class
Sign up to request clarification or add additional context in comments.

5 Comments

If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int. -- I can't really to understand the sence of this rule, can you please explain the sence?
And I need to remember that I should call my method with explicit specified type parameter: ReturnNullOrNotNullTSource1<DateOnly?>(new DateOnly(0001, 1, 1)); so if I forger it and make this: ReturnNullOrNotNullTSource1(new DateOnly(0001, 1, 1)); then it still compiles but work completely wrong. What can I do make it uncompiled when I don't specify type parameter ?
I tried to use rule: "You can specify different behavior using constraints: The notnull constraint means that T must be a non-nullable reference type, or a non-nullable value type.". I added constraint "where TSource1 : notnull", but result is the same - 01.01.0001 instead of null. Constraint "TSource1 : struct" is not suitable for me because I want to allow reference types also.
Please read the first paragraph of the “Generics” section. The sense is the implementation of T? for reference types (T) and value types (Nullable<T>) are different, the compiler is not able to generate the same instructions to cover these 2 different kind of types, and allowing this also violates the C# standards.
You can define overload methods for both types, but because the return type is not a part of method signature, you need to use an out parameter to return the nullable object, check my update.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.