Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't make a Not-Null Type Parameter Nullable in an Abstract Class #3101

Closed
the-avid-engineer opened this issue Jan 15, 2020 · 14 comments
Closed

Comments

@the-avid-engineer
Copy link

the-avid-engineer commented Jan 15, 2020

With the introduction of non-nullable reference types, I've hit a case a few times where I want to create a class like this one:

    public abstract class Foo<T> where T : notnull
    {
        public T? Value { get; set; }
    }

But I cannot because the compiler complains:

A nullable type parameter must be known to be a value type or a non-nullable reference type.

Non-nullable property 'Value' is uninitialized. Consider declaring the property as nullable.

Are there any plans to allow this? Am I missing something? I have my suspicions on why it was left out, but it feels like this could be allowed to some degree.

@the-avid-engineer the-avid-engineer changed the title Can't make a Not-Null Type Parameter Nullable Can't make a Not-Null Type Parameter Nullable in an Abstract Class Jan 15, 2020
@Joe4evr
Copy link
Contributor

Joe4evr commented Jan 15, 2020

You have to apply some attributes and provide an initializer:

using System.Diagnostics.CodeAnalysis;

public abstract class Foo<T> where T : notnull
{
    [MaybeNull, AllowNull]
    public T Value { get; set; } = default!;
}

SharpLab

@333fred
Copy link
Member

333fred commented Jan 15, 2020

See #3035.

@the-avid-engineer
Copy link
Author

See #3035.

My use case is specifically for type parameters constrained to notnull; it looks like this proposal is about type parameters that are not constrained?

@the-avid-engineer
Copy link
Author

You have to apply some attributes and provide an initializer:

using System.Diagnostics.CodeAnalysis;

public abstract class Foo<T> where T : notnull
{
    [MaybeNull, AllowNull]
    public T Value { get; set; } = default!;
}

SharpLab

That doesn't produce concrete classes the way I would expect.

public class Bar : Foo<string> { }
public class Baz : Foo<int> {}

I would expect the type of Bar.Value to be string? which works out fine in your example, but the type of Baz.Value is not Nullable<int>, right?

@HaloFour
Copy link
Contributor

@the-avid-engineer

When using nullable types with generics in such a manner you have to constraint T to either class or struct. The compiler can't conditionally make the type T or Nullable<T> based on the type of T.

@333fred
Copy link
Member

333fred commented Jan 15, 2020

Ah, so you're looking for int?. That is not a thing we are going to be able to solve in C# today, or possibly ever. Read the conclusion section here: https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-01-08.md#unconstrained-type-parameter-annotation-t, specifically the part on alternative syntax.

@the-avid-engineer
Copy link
Author

@HaloFour @333fred Thanks

@the-avid-engineer
Copy link
Author

the-avid-engineer commented Jan 20, 2020

I do wonder if the compiler could do something different when T is constrained to notnull

If where T : struct means T? is shorthand for Nullable<T>,

Then where T : notnull would mean T? is shorthand for NotNull<T>?

And NotNull<T> is similar to Nullable except it's a class and its Value property is constrained to a notnull type instead of a struct type.

public class NotNull<T> where T : notnull
{
    public T Value { get; set; }
}

I could definitely do this on my own, but writing NotNull<T>? isn't as pretty as T?

@mikernet
Copy link

Pretty sure T?? will also work for notnull. If not then that needs to change. I think by "unconstrained" they mean not constrained to struct or class. There's no reason it shouldn't also work with notnull.

@the-avid-engineer
Copy link
Author

the-avid-engineer commented Jan 23, 2020

Pretty sure T?? will also work for notnull. If not then that needs to change. I think by "unconstrained" they mean not constrained to struct or class. There's no reason it shouldn't also work with notnull.

From what I gather, T?? is basically a way to tell the compiler the value can be default(T) for a struct/enum or null for a class/delegate. That probably suffices for most use cases, and it will be a very nice feature.

But.. a nice benefit of using T? = Nullable<T> is you can have all possible values of T plus one (null). With something like T? = NotNull<T>? from above, if T is a struct you get the same benefit. With T?? you don’t get that extra possible value for a struct.

@YairHalberstadt
Copy link
Contributor

Closing as now valid in C# 9

@the-avid-engineer
Copy link
Author

Closing as now valid in C# 9

Wait what?? :O

@the-avid-engineer
Copy link
Author

Oh I see. It's not invalid syntax anymore. ( It doesn't do what I was hoping for, though :( )

@sweeperq
Copy link

Came across this while trying to implement an abstract base class to process entities with various types of keys (int, long, guid, etc). I constrained my keys with where TId : notnullable, IEquatable<TId>. Don't get any errors, but setting up a property like public TId? Id { get; set; } ends up resulting in int instead of int? for the type at runtime. It does the same thing with TId[]?; it ends up as int[] instead of int[]?. Fortunately, the arrays do not result in an error when the TId[]? is converted to int[] and gets assigned a default value of null.

In my particular case I was able to work around the issue. I changed my processing code from if(Id != null) to if(Id != null || !Id.Equals(default)) . I may swap out TId? with TId and just do the default check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants