I want to create a class like Nullable except for the moment that it could work with classes and structures:
public class MyWrapper<T>
{
public MyWrapper(T value)
{
Value = value;
}
public T Value { get; set; }
}
Then I want to add implicit conversion of Nullable to MyWrapper:
public static implicit operator MyWrapper<T> (Nullable<T> value)
{
return new MyWrapper<T>(value);
}
And of course, it fails because of Nullable restrictions:
Only non-nullable value type could be underlying of 'System.Nullable'
It is a pretty understandable error but in theory, I could convert any Nullable to MyWrapper, because of restrictions of Nullable harder than MyWrapper's restrictions.
So it there any workaround for Nullable to MyWrapper implicit conversion?
Why do I need to use MyWrapper for classes?
We use some sort of bad GraphQl on the backend and sent updation objects like this:
class UpdateProductRequest
{
public MyWrapper<string> Country {get;set;}
public MyWrapper<string> Title {get;set;}
}
So
new UpdateProductRequest
{
Title = "new title"
}
update the title, but not the country.
You can define implicit casting from underlying type to MyWrapper:
public static implicit operator MyWrapper<T> (T value)
{
return new MyWrapper<T>(value);
}
Now with such operator you can do this:
MyWrapper<int> w = new int?(5);
MyWrapper<int> w2 = (int?)null; //here casting method is not called
So initially the Nullable is casted down to the underlying type and then to MyWrapper. If it's null there is just null assignment to variable of typeMyWrapper which is class so it's valid. Nullable has special treatment by the compiler so it may look like a magic but it works.
Related
Given the following class
public class Component<TValue>
{
public TValue? MaxValue { get; set; }
public TValue? MinValue { get; set; }
public TValue Value { get; set; } = default!;
public override string ToString() =>
$"MinValue = {MinValue}, MaxValue = {MaxValue}, Value = {Value}";
}
Why is it that when I create an instance of Component<int> the MinValue and MaxValue properties are int instead of Nullable<int>?
static void Main(string[] args)
{
var instance = new Component<int>();
instance.Value = 42;
Console.WriteLine(instance.ToString());
}
I could get it to behave how I want to by adding where TValue: struct, but I am writing a Blazor component that needs to work with any numeric type (including nullables) so I can't add that constraint.
I'd like to know the logic behind why TValue? for an int should not be compiled as Nullable<Int32>.
You did not constrain your class's type parameter neither to struct nor to class.
When you create new instance of Component with generic type parameter which is not nullable (in your case 'int') all of the properties using this generic type, will become int.
If you want your TValue to be nullable, create instance providing 'int?' as a generic type parameter.
When the type parameter T in syntax T? is not constrained to a struct it takes on the nullable reference type meaning of T?. In the cases where T is instantiated with a reference or interface type then the result will continue to be a nullable reference type. For instance if it's instantiated with string the value is treated as string?. In the case T is instantiated with a value type the ? is effectively dropped as it has no meaning.
If the desire is to have T? mean a nullable value type then T must be constrained to struct
If the desire is to have T? mean Nullable<T> when instantiated with a value type and T? when instiated with a reference type, then unfortunately there is no such syntax for that. It would require significant runtime work to provide
This question already has answers here:
Nullable reference types: How to specify "T?" type without constraining to class or struct
(3 answers)
T?, nullable type parameter in C# 9
(2 answers)
Closed 8 months ago.
I have the following struct:
struct Generic<T>
{
public T Property { get; init; }
public Generic(T property) { Property = property; }
public static implicit operator T?(Generic<T> x)
=> x.GetHashCode() == 42 ? null : x.Property;
}
The compiler underlines the null in the implicit conversion and reports error CS0403:
Cannot convert null to type parameter T because it could be a non-nullable value type. Consider using default(T) instead.
But I do not define a conversion to T, I define it to T?! Why does the compiler not see this? And what can I do to get rid of the compiler error?
Strangely, these two nongeneric versions - one with T being a struct, one with T being a class - do not give any compiler errors:
struct NongenericStruct
{
public int Property { get; init; }
public NongenericStruct(int property) { Property = property; }
public static implicit operator int?(NongenericStruct x)
=> x.GetHashCode() == 42 ? null : x.Property;
}
struct NongenericClass
{
public string Property { get; init; }
public NongenericClass(string property) { Property = property; }
public static implicit operator string?(NongenericClass x)
=> x.GetHashCode() == 42 ? null : x.Property;
}
A nullable reference type is not the same as a nullable value type.
As the generic type T has no constraint, the compiler cannot explicitly convert null into either a Nullable<T> (struct) or a T? (class).
In order to fix this you have to explicitly tell the compiler if it's a reference or a value type with a generic type constraint.
// reference type
where T : class
// value type
where T : struct
I am trying to make a model class in C# in which i require object/List properties as optional property:
public class Customer
{
[JsonProperty("Custid")]
public string CustId { get; set; }
[JsonProperty("CustName")]
public string CustName { get; set; }
}
public class Store
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("Name")]
public string? Name { get; set; }
[JsonProperty("Customer")]
public List<Customer>? Customers{ get; set; } *//Error 1*
[JsonProperty("OtherProperty")]
public object? OtherProperty{ get; set; } *//Error 2*
}
The above code is giving error as :-
Error 1: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
Error 2: The type 'List' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
Please Explain me the above scenario and provide me with the alternate solution.
string, List and object are all reference types. Those are nullable by default. The Nullable type (e.g. int? is a shorthand for Nullable<int>) is only used for value types.
In C# 8.0, a new feature was introduced that allows for non-nullable reference types - i.e. reference types that explicitly disallow null assignment. This is an opt-in feature - you can enable it to allow you to more clearly show intent about the references. If you use this, the syntax used to define nullable reference types is the same as for nullable value types:
string nonNullableString = null; // Error
string? nullableString = null; // Ok
Keep in mind that enabling non-nullable reference types means that all of the reference types that aren't followed by ? will be non-nullable; this might require you to make lots of changes in your application.
So there's your two choices. Either enable non-nullable reference types, and then you need to explicitly mark types that you want to have nullable, or stick with nullable reference types, and just use string instead of string? for the same result. I would encourage the use of non-nullable types by default, since it holds some promise for avoiding an entire class of very common programming mistakes.
If you aren't using C# 8:
object? doesn't exists. object is already nullable.
List<Customer>? doesn't exists. List<Customer> is already nullable.
If you want to use nullable reference types you must update your compiler version!
The Nullable<T> type requires that T is a non-nullable value type, for example int or DateTime. Reference types like string or List can already be null. There would be no point in allowing things like Nullable<List<T>> so it is disallowed.
Looking Zoran Horvats courses at PluralSight, I'm currently implementing a Maybe type, a bit like Zoran has on its GitHub account: https://github.com/zoran-horvat/option
Generally, a Maybe is a wrapper around objects, which are either set or have a null value, avoiding null reference exceptions.
To make the code a bit shorter, I would like to use implicit conversion to map the values / nulls to their corresponding maybe types. Here an example of my code:
public void Hook(Maybe<Action<Keys>> onKeyDown, Maybe<Action<Keys>> onKeyUp)
{
_keyDownCallback = onKeyDown;
_keyUpCallback = onKeyUp;
_hookService.Hook(HookType.KeyBoardLowLevel, OnHookReceived);
}
As you can see, you can hook and pass two optional callbacks, one for keyDown and one for keyUp. I would like to pass code like this:
nativeKeyboardHookService.Hook(new Action<Keys>(OnNativeKeyDown), null);
The implicit conversion on the Maybe is currently implemented like this:
public static implicit operator Maybe<T>(T value)
{
return ToMaybe(value);
}
public static implicit operator T(Maybe<T> maybe)
{
return ToT(maybe);
}
public static Maybe<T> ToMaybe(T value)
{
if (value == null)
{
return new None<T>();
}
return new Some<T>(value);
}
public static T ToT(Maybe<T> maybe)
{
return maybe.Evaluate(
value => value,
() => default(T));
}
My question: It works fine, if I pass an actual object, mapping it to an Maybe, but if I pass NULL, I still get a NULL object, not a None object. Am I doing here something wrong or is it just not possible? I didn't find any further information regarding such a conversion.
When you pass null to Hook() that's literally all you are doing because your implicit casts aren't being invoked at all. That's because null is a valid value for a reference type, and thus no need to cast.
You can't change Maybe to a struct if you want to keep Some and None because then these would have to be structs too, which means you run into the issue that you can't inherit structs.
You can't implement a common IMaybe<T> interface either because interfaces can't be used with casts.
What I recommend is keep your behavior as is, but don't use null. Instead of passing null, pass something else like Maybe<T>.None:
class Maybe<T>
{
public static Maybe<T> None { get; } = new None<T>();
}
void Hook(..., Maybe<T>.None) { ... }
Or None<T>.Instance:
class None<T>
{
public static None<T> Instance{ get; } = new None<T>();
}
void Hook(..., None<T>.Instance) { ... }
This has the advantage of being more readable and explicit.
Your Maybe<T> is still a reference type, so null is a valid value for it:
Maybe<string> foo = null;
If you want to prevent that, you will need to make it a value type, for example something like this:
public struct Maybe<T>
{
public T Value { get; }
public bool IsEmpty => Value == null;
public Maybe(T value)
{
Value = value;
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
}
Then you can pass null to a method expecting a Maybe<T> and it will properly construct an empty Maybe<T> object.
But note that it being a value type, this now means that it is copied on every method call, so it has a different behavior to a reference type implementation.
In the end, you cannot really implement this nicely in C# simply because there is the null reference in the language. It’s only with C# 8’s nullable reference types that you will be able to prevent nulls altogether.
I'm trying to build a class which behaves like Nullable<T>, specifically the way I can access the underlying value of Nullable<T> class without having to explicitly call nullable.Value.
In the following example lines check1 & check2 both work.
Nullable<DateTime> nullable = new DateTime();
bool check1 = nullable >= DateTime.Now; //Works
bool check2 = nullable.Value >= DateTime.Now; //Works
I built my own class TrackedValue which remembers if the value it wraps has been changed. I've based mine on Nullable<T> and built the implicit & explicit operators.
Nullable<T> definition
public struct Nullable<T> where T : struct
{
public Nullable(T value);
public static explicit operator T(T? value);
public static implicit operator T?(T value);
...
}
TrackedValue<T> definition
public class TrackedValue<T> : IChangeTracking
{
...
T trackedValue;
public T Value
{
get
{
return this.trackedValue;
}
set
{
this.trackedValue = value;
}
}
public static explicit operator T(TrackedValue<T> value)
{
return value.Value;
}
public static implicit operator TrackedValue<T>(T value)
{
return new TrackedValue<T>() { Value = value };
}
}
So I was expecting the following to work, however check3 will not compile because of:
Argument 1: cannot convert from 'TrackedValue<System.DateTime>' to 'System.DateTime'
TrackedValue<DateTime> trackedValue = new DateTime();
bool check3 = trackedValue >= DateTime.Now; //Does not work
bool check4 = trackedValue.Value >= DateTime.Now; //Works
Any pointers would be appreciated.
That line specifically doesn't work because it requires an implicit conversion but you have it marked as explicit.
public class TrackedValue<T> : IChangeTracking
{
T trackedValue;
public T Value
{
get
{
return this.trackedValue;
}
set
{
this.trackedValue = value;
}
}
public static implicit operator T(TrackedValue<T> value)
{
return value.Value;
}
public static implicit operator TrackedValue<T>(T value)
{
return new TrackedValue<T>() { Value = value };
}
}
But naturally, you want to mimic the Nullable<T> model. So why does Nullable<T> <= T work implicitly but not yours? I believe it comes from the C# compiler itself. Eric Lippert has an excellent blog series on how Nullables are compiled/optimized.
From what I understand, the compiler itself alters the written code/IL to a different instruction set altogether. Eric's third entry on the series starts to demonstrate this. This is because it handles special cases for nulls generally I believe.
I'm not sure if you can work around this, but perhaps the simplest way is to simply mark the one conversion operator there as implicit instead and hope it doesn't cause any major issues for you with regards to consistency between TrackedValue<T> and Nullable<T>.
EDIT: One of those items of inconsistency will be say how comparisons are made.
Consider your line bool check3 = trackedValue >= DateTime.Now in the case that trackedValue is null. For a Nullable<DateTime> it goes kind of like this (note that this is not exactly what it is, see Eric's series. This is just for communicating the concept):
check3 = trackedValue.HasValue ? trackedValue.Value >= DateTime.Now : false;
The compiler avoids even calling the conversion operator. Yours on the other hand would attempt to run your implicit conversion (assuming you switch it to implicit) which could result in a NullReferenceException which is frowned upon (implicit operators should not throw exceptions). The reason why Nullable<T> defines the conversion operator as explicit is because for those times that you do directly cast (e.g., DateTime casted = (DateTime)myNullableDateTime;) can throw an exception if the value is null.
You declared your conversion operator explicit, so while bool check3 = trackedValue >= DateTime.Now; //Does not work, this should work:
bool check3 = (DateTime)trackedValue >= DateTime.Now;
The other course of action of course is to declare it implicit.
change the operator T to implicit:
public static implicit operator T(TrackedValue<T> value)
{
return value.Value;
}