I have implemented an Option type for some project of mine like this:
public abstract Option<T> {}
public class None<T> : Option<T>
public class Some<T> : Option<T>
{
public T Value { get; }
public Some(T value)
{
Value = value;
}
}
For finding out if an Option contains a value I use this extension method which makes use of pattern matching:
public static bool TryGetValue<T>(this Option<T> option, out T value)
{
if (option is Some<T> some)
{
value = some.Value;
return true;
}
value = default;
return false;
}
I now get the following warning for return default;
Cannot convert null literal to non nullable reference or unconstrained type parameter
It is impossible for me to restrict the generic parameter T to class or struct.
For example, if I restricted the generic parameter to class, I couldn't generate Option<int?> instances as the Nullable<int> type is a struct itself. Declaring the out parameter as nullable via postfixing ? is also not a solution as it seems.
To me the type system is somewhat broken or not thoroughly thought through at this stage. Either Nullable should have been a class or there needs to be a generic parameter restriction like:
public static bool TryGetValue<T>(this Option<T> option, out T value)
where T : nullable [...]
Is there another approach that might be suitable for this issue? What am I missing?
Use the MaybeNullWhenAttribute or the NotNullWhenAttribute. I recommend MaybeNullWhen since it works even on a type parameter not constrained to a struct or reference type.
public static bool TryGetValue<T>(this Option<T> option, [MaybeNullWhen(false)] out T value)
{
if (option is Some<T> some)
{
value = some.Value;
return true;
}
value = default;
return false;
}
Usage:
if(option.TryGetValue(out var value))
{
value.SomeMethod(); // no warning - value is known to be non-null here
}
value.SomeMethod(); // warning - value may be null here.
The attribute is not available pre .Net standard 2.1/.new core 3.0, but you can manually define it yourself if it's not available. Make sure it's internal, as otherwise if another library also defines it as public and someone inherits from both libraries it will cause conflicts:
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
(taken from https://github.com/dotnet/runtime/blob/6077cf01f951a711a26a8d5970b211b6031b5158/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs#L45-L60)
There is no pretty way to have a completely generic solution as of today with C# 8.0.
[NotNullWhen()] attribute is one step forward, but then we will run into following:
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.
I'd say this is a major pain point with nullables now. I hope it will be addressed in 8.1 or something...
Relevant discussion - https://github.com/dotnet/csharplang/issues/2194 - allow generic methods to specify T? without constraining to class or struct.
As a workaround, multiple extension method copies with required where constraints can be made to cover all possible types.
Since the issue #1628 was fixed, it is now possible to have all overloads in a single extension class. But it still require to double the number of extension methods for every independent generic out parameter. Yikes!
From a quick look, I couldn't find an opened issue specific to the out parameters. Might worth to bring this particular use case to csharplang github issues if that wasn't done yet.
Update: I made a comment in the above-mentioned issue with my take on it.
Related
I'm trying to create a type similar to Rust's Result or Haskell's Either and I've got this far:
public struct Result<TResult, TError>
where TResult : notnull
where TError : notnull
{
private readonly OneOf<TResult, TError> Value;
public Result(TResult result) => Value = result;
public Result(TError error) => Value = error;
public static implicit operator Result<TResult, TError>(TResult result)
=> new Result<TResult, TError>(result);
public static implicit operator Result<TResult, TError>(TError error)
=> new Result<TResult, TError>(error);
public void Deconstruct(out TResult? result, out TError? error)
{
result = (Value.IsT0) ? Value.AsT0 : (TResult?)null;
error = (Value.IsT1) ? Value.AsT1 : (TError?)null;
}
}
Given that both types parameters are restricted to be notnull, why is it complaining (anywhere where there's a type parameter with the nullable ? sign after it) that:
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.
?
I'm using C# 8 on .NET Core 3 with nullable reference types enabled.
Basically you're asking for something that can't be represented in IL. Nullable value types and nullable reference types are very different beasts, and while they look similar in source code, the IL is very different. The nullable version of a value type T is a different type (Nullable<T>) whereas the nullable version of a reference type T is the same type, with attributes telling the compiler what to expect.
Consider this simpler example:
public class Foo<T> where T : notnull
{
public T? GetNullValue() =>
}
That's invalid for the same reason.
If we constraint T to be a struct, then the IL generated for the GetNullValue method would have a return type of Nullable<T>.
If we constraint T to be a non-nullable reference type, then the IL generated for the GetNullValue method would have a return type of T, but with an attribute for the nullability aspect.
The compiler can't generate IL for a method which has a return type of both T and Nullable<T> at the same time.
This is basically all the result of nullable reference types not being a CLR concept at all - it's just compiler magic to help you express intentions in code and get the compiler to perform some checking at compile-time.
The error message isn't as clear as it might be though. T is known to be "a value type or non-nullable reference type". A more precise (but significantly wordier) error message would be:
A nullable type parameter must be known to be a value type, or known to be a non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.
At that point the error would reasonably apply to our code - the type parameter is not "known to be a value type" and it's not "known to be a non-nullable reference type". It's known to be one of the two, but the compiler needs to know which.
The reason for the warning is explained in the section The issue with T? of Try out Nullable Reference Types. Long story short, if you use T? you have to specify whether the type is a class or struct. You may end up creating two types for each case.
The deeper problem is that using one type to implement Result and hold both Success and Error values brings back the same problems Result was supposed to fix, and a few more.
The same type has to carry a dead value around, either the type or the error, or bring back nulls
Pattern matching on the type isn't possible. You'd have to use some fancy positional pattern matching expressions to get this to work.
To avoid nulls you'll have to use something like Option/Maybe, similar to F#'s Options. You'd still carry a None around though, either for the value or error.
Result (and Either) in F#
The starting point should be F#'s Result type and discriminated unions. After all, this already works on .NET.
A Result type in F# is :
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
The types themselves only carry what they need.
DUs in F# allow exhaustive pattern matching without requiring nulls :
match res2 with
| Ok req -> printfn "My request was valid! Name: %s Email %s" req.Name req.Email
| Error e -> printfn "Error: %s" e
Emulating this in C# 8
Unfortunately, C# 8 doesn't have DUs yet, they're scheduled for C# 9. In C# 8 we can emulate this, but we lose exhaustive matching :
#nullable enable
public interface IResult<TResult,TError>{}
struct Success<TResult,TError> : IResult<TResult,TError>
{
public TResult Value {get;}
public Success(TResult value)=>Value=value;
public void Deconstruct(out TResult value)=>value=Value;
}
struct Error<TResult,TError> : IResult<TResult,TError>
{
public TError ErrorValue {get;}
public Error(TError error)=>ErrorValue=error;
public void Deconstruct(out TError error)=>error=ErrorValue;
}
And use it :
IResult<double,string> Sqrt(IResult<double,string> input)
{
return input switch {
Error<double,string> e => e,
Success<double,string> (var v) when v<0 => new Error<double,string>("Negative"),
Success<double,string> (var v) => new Success<double,string>(Math.Sqrt(v)),
_ => throw new ArgumentException()
};
}
Without exhaustive pattern matching, we have to add that default clause to avoid compiler warnings.
I'm still looking for a way to get exhaustive matching without introducing dead values, even if they are just an Option.
Option/Maybe
Creating an Option class by the way that uses exhaustive matching is simpler :
readonly struct Option<T>
{
public readonly T Value {get;}
public readonly bool IsSome {get;}
public readonly bool IsNone =>!IsSome;
public Option(T value)=>(Value,IsSome)=(value,true);
public void Deconstruct(out T value,out bool isSome)=>(value,isSome)=(Value,IsSome);
}
//Convenience methods, similar to F#'s Option module
static class Option
{
public static Option<T> Some<T>(T value)=>new Option<T>(value);
public static Option<T> None<T>()=>default;
}
Which can be used with :
string cateGory = someValue switch { Option<Category> (_ ,false) =>"No Category",
Option<Category> (var v,true) => v.Name
};
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.
I want to create a generic class that has a member of type T. T may be a class, a nullable class, a struct, or a nullable struct. So basically anything. This is a simplified example that shows my problem:
#nullable enable
class Box<T> {
public T Value { get; }
public Box(T value) {
Value = value;
}
public static Box<T> CreateDefault()
=> new Box<T>(default(T));
}
Due to using the new #nullable enable feature I get the following warning: Program.cs(11,23): warning CS8653: A default expression introduces a null value when 'T' is a non-nullable reference type.
This warning makes sense to me. I then tried to fix it by adding a ? to the property and constructor parameter:
#nullable enable
class Box<T> {
public T? Value { get; }
public Box(T? value) {
Value = value;
}
public static Box<T> CreateDefault()
=> new Box<T>(default(T));
}
But now I get two errors instead:
Program.cs(4,12): 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.
Program.cs(6,16): 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.
However, I don't want to add a constraint. I don't care if T is a class or a struct.
An obvious solution is to wrap the offending members under a #nullable disable directive. However, like #pragma warning disable, I'd like to avoid doing that unless it's necessary. Is there another way in getting my code to compile without disabling the nullability checks or the CS8653 warning?
$ dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.0.100-preview4-011223
Commit: 118dd862c8
What to do if you are using C# 9
In C# 9, you can use T? on an unconstrained type parameter to indicate that the type is always nullable when T is a reference type. In fact, the example in the original question "just works" after adding ? to the property and constructor parameter. See the following example to understand what behaviors you may expect for different kinds of type arguments to Box<T>.
var box1 = Box<string>.CreateDefault();
// warning: box1.Value may be null
box1.Value.ToString();
var box2 = Box<string?>.CreateDefault();
// warning: box2.Value may be null
box2.Value.ToString();
var box3 = Box<int>.CreateDefault();
// no warning
box3.Value.ToString();
var box4 = Box<int?>.CreateDefault();
// warning: 'box4.Value' may be null
box4.Value.Value.ToString();
What to do if you are using C# 8
In C# 8, it is not possible to put a nullable annotation on an unconstrained type parameter (i.e. that is not known to be of a reference type or value type).
As discussed in the comments on this question, you will probably need to take some thought as to whether a Box<string> with a default value is valid or not in a nullable context and potentially adjust your API surface accordingly. Perhaps the type has to be Box<string?> in order for an instance containing a default value to be valid. However, there are scenarios where you will want to specify that properties, method returns or parameters, etc. could still be null even though they have non-nullable reference types. If you are in that category, you will probably want to make use of nullability-related attributes.
The MaybeNull and AllowNull attributes have been introduced to .NET Core 3 to handle this scenario.
Some of the specific behaviors of these attributes are still evolving, but the basic idea is:
[MaybeNull] means that the output of something (reading a field or property, a method return, etc.) could be null.
[AllowNull] means that the input to something (writing a field or property, a method parameter, etc.) could be null.
#nullable enable
using System.Diagnostics.CodeAnalysis;
class Box<T>
{
// We use MaybeNull to indicate null could be returned from the property,
// and AllowNull to indicate that null is allowed to be assigned to the property.
[MaybeNull, AllowNull]
public T Value { get; }
// We use only AllowNull here, because the parameter only represents
// an input, unlike the property which has both input and output
public Box([AllowNull] T value)
{
Value = value;
}
public static Box<T> CreateDefault()
{
return new Box<T>(default);
}
public static void UseStringDefault()
{
var box = Box<string>.CreateDefault();
// Since 'box.Value' is a reference type here, [MaybeNull]
// makes us warn on dereference of it.
_ = box.Value.Length;
}
public static void UseIntDefault()
{
// Since 'box.Value' is a value type here, we don't warn on
// dereference even though the original property has [MaybeNull]
var box = Box<int>.CreateDefault();
_ = box.Value.ToString();
}
}
Please see https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types for more information, particularly the section "the issue with T?".
Jeff Mercado raised a good point in the comments:
I think you have some conflicting goals here. You want to have the notion of a default box but for reference types, what else is an appropriate default? The default is null for reference types which directly conflicts with using nullable reference types. Perhaps you will need to constrain T to types that could be default constructed instead (new()).
For example, default(T) for T = string would be null, since at runtime there is no distinction between string and string?. This is a current limitation of the language feature.
I have worked around this limation by creating separate CreateDefault methods for each case:
#nullable enable
class Box<T> {
public T Value { get; }
public Box(T value) {
Value = value;
}
}
static class CreateDefaultBox
{
public static Box<T> ValueTypeNotNull<T>() where T : struct
=> new Box<T>(default);
public static Box<T?> ValueTypeNullable<T>() where T : struct
=> new Box<T?>(null);
public static Box<T> ReferenceTypeNotNull<T>() where T : class, new()
=> new Box<T>(new T());
public static Box<T?> ReferenceTypeNullable<T>() where T : class
=> new Box<T?>(null);
}
This seems type safe to me, at the cost of more ugly call sites (CreateDefaultBox.ReferenceTypeNullable<object>() instead of Box<object?>.CreateDefault()). In the example class I posted I'd just remove the methods completely and use the Box constructor directly. Oh well.
class Box<T> {
public T! Value { get; }
public Box(T! value) {
Value = value;
}
public static Box<T> CreateDefault()
=> new default!;
}
What does null! statement mean?
I came across this problematic quite often: I like to overload some method with same parameters for different return types, but .NET refuses generic constraints to sealed classes/primitives. I'll refer to this pattern as phantom generics.
I know an ugly workaround: Putting every single interface the type implements behind the where statement.
My Question: Is there any way to use explicit types in generics to illustrate the return type and keep methods distinct?
Here is my code:
public static class Reinterpret {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float Cast<T>(int value) where T : float { //'float' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((float*)&value); //reinterpret the bytes of 'value' to a float
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float Cast<T>(uint value) where T : float { //'float' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((float*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe double Cast<T>(long value) where T : double { //'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((double*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe double Cast<T>(ulong value) where T : double { //'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((double*)&value);
}
}
Here's one slightly different way of approach it:
// Constraints just to be vaguely reasonable.
public static class Reinterpret<T> where T : struct, IComparable<T>
{
public T Cast(int value) { ... }
public T Cast(uint value) { ... }
public T Cast(float value) { ... }
public T Cast(double value) { ... }
// etc
}
For the implementation, you could just have a Func<int, T> field, a Func<double, T> field etc, and then have a big static constructor:
static Reinterpret()
{
if (typeof(T) == typeof(int))
{
// Assign all the fields using lambda expressions for ints.
// The actual assignment might be tricky, however - you may
// need to resort to some ghastly casting, e.g.
castDouble = (Func<double, T>)(Delegate)(Func<double, int>)
x => *((double*)&value;
}
....
}
Then for any type you didn't want to support, the fields would be null. Each Cast method would look like:
if (castIntMethod != null)
{
return castInt(value);
}
throw new InvalidOperationException("...");
To be honest, this isn't something I'd really want to do. I'd generally just use BitConverter. But it's an option.
Generics are not templates. They do not act like templates. They cannot be made to act like templates.
A "phantom" generic parameter is not going to help you simulate templates (and reinterpret_cast is not an actual template, anyway), because you soon are going to run into the fact that generics do not support specialization.
In particular, you asked "Is there any way to use explicit types in generics to ... keep methods distinct?" and commented that "the generic constraints ... keeps [sic] the methods distinct". But they actually do not. These methods are distinct only because the argument types are different. Generics are computed from overloads, they do not influence overloading.
This causes a compile-time exception:
public sealed class ValidatesAttribute<T> : Attribute
{
}
[Validates<string>]
public static class StringValidation
{
}
I realize C# does not support generic attributes. However, after much Googling, I can't seem to find the reason.
Does anyone know why generic types cannot derive from Attribute? Any theories?
Well, I can't answer why it's not available, but I can confirm that it's not a CLI issue. The CLI spec doesn't mention it (as far as I can see) and if you use IL directly you can create a generic attribute. The part of the C# 3 spec that bans it - section 10.1.4 "Class base specification" doesn't give any justification.
The annotated ECMA C# 2 spec doesn't give any helpful information either, although it does provide an example of what's not allowed.
My copy of the annotated C# 3 spec should arrive tomorrow... I'll see if that gives any more information. Anyway, it's definitely a language decision rather than a runtime one.
EDIT: Answer from Eric Lippert (paraphrased): no particular reason, except to avoid complexity in both the language and compiler for a use case which doesn't add much value.
An attribute decorates a class at compile-time, but a generic class does not receive its final type information until runtime. Since the attribute can affect compilation, it has to be "complete" at compile time.
See this MSDN article for more information.
I don't know why it's not allowed, but this is one possible workaround
[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
public ClassDescriptionAttribute(Type KeyDataType)
{
_KeyDataType = KeyDataType;
}
public Type KeyDataType
{
get { return _KeyDataType; }
}
private Type _KeyDataType;
}
[ClassDescriptionAttribute(typeof(string))]
class Program
{
....
}
This is not truly generic and you still have to write specific attribute class per type, but you may be able to use a generic base interface to code a little defensively, write lesser code than otherwise required, get benefits of polymorphism etc.
//an interface which means it can't have its own implementation.
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
T Value { get; } //or whatever that is
bool IsValid { get; } //etc
}
public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
//...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
//...
}
[ValidatesString]
public static class StringValidation
{
}
[ValidatesInt]
public static class IntValidation
{
}
Generic Attributes are available since C# 11. Now, this is possible:
[GenericAttribute<int>()]
public int Method();
However, this is not possible yet:
[GenericAttribute<T>()]
public int Method<T>(T param);
T is not known at compile time.
Also,
The type arguments must satisfy the same restrictions as the typeof
operator. Types that require metadata annotations aren't allowed. For
example, the following types aren't allowed as the type parameter:
dynamic
string? (or any nullable reference type)
(int X, int Y) (or any other tuple types using C# tuple syntax).
These types aren't directly represented in metadata. They include annotations that
describe the type. In all cases, you can use the underlying type
instead:
object for dynamic.
string instead of string?.
ValueTuple<int, int> instead of (int X, int Y).
Source: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes
This is a very good question. In my experience with attributes, I think the constraint is in place because when reflecting on an attribute it would create a condition in which you would have to check for all possible type permutations: typeof(Validates<string>), typeof(Validates<SomeCustomType>), etc...
In my opinion, if a custom validation is required depending on the type, an attribute may not be the best approach.
Perhaps a validation class that takes in a SomeCustomValidationDelegate or an ISomeCustomValidator as a parameter would be a better approach.
This is not currently a C# language feature, however there is much discussion on the official C# language repo.
From some meeting notes:
Even though this would work in principle, there are bugs in most
versions of the runtime so that it wouldn't work correctly (it was
never exercised).
We need a mechanism to understand which target runtime it works on. We
need that for many things, and are currently looking at that. Until
then, we can't take it.
Candidate for a major C# version, if we can make a sufficient number
of runtime versions deal with it.
Generic attributes are supported since .NET 7 and C# 11 (in preview in .NET 6 and C# 10).
My workaround is something like this:
public class DistinctType1IdValidation : ValidationAttribute
{
private readonly DistinctValidator<Type1> validator;
public DistinctIdValidation()
{
validator = new DistinctValidator<Type1>(x=>x.Id);
}
public override bool IsValid(object value)
{
return validator.IsValid(value);
}
}
public class DistinctType2NameValidation : ValidationAttribute
{
private readonly DistinctValidator<Type2> validator;
public DistinctType2NameValidation()
{
validator = new DistinctValidator<Type2>(x=>x.Name);
}
public override bool IsValid(object value)
{
return validator.IsValid(value);
}
}
...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }
[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }