-
Notifications
You must be signed in to change notification settings - Fork 0
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
Better NullReferenceException for non-null properties #6
Comments
So here is a slight better solution without needing the Source Generator magic: public static class Safe {
public static T Get<T>(T? value) {
if (value != null) {
return value;
}
var method = new StackFrame(1).GetMethod();
if (method == null) { // This if may be skipped IMO
throw new NullReferenceException(
"Unexpectedly failed to find info of getting a null value.");
}
throw new NullReferenceException(
$"Property {method.Name[4..]} of class {method.DeclaringType} is expected to be non-null, but is actually null.");
}
public static void Set<T>(ref T field, T? value) {
if (value == null) {
var method = new StackFrame(1).GetMethod();
if (method == null) { // This if may be skipped IMO
throw new NullReferenceException(
"Unexpectedly failed to find info of setting a null value.");
}
throw new NullReferenceException(
$"Cannot assign null value to non-null property {method.Name[4..]} of class {method.DeclaringType}.");
}
field = value;
}
} And the usage looks like: private string? _id;
public string Id {
get => Safe.Get(_id);
set => Safe.Set(ref _id, value);
} It's also possible to implement the |
For struct types like int to work. You may need to add public static T Get<T>(T? value) where T : struct {
if (value != null) {
return value.Value;
}
var method = new StackFrame(1).GetMethod();
if (method == null) {
throw new NullReferenceException(
"Unexpectedly failed to find info of getting a null value.");
}
throw new NullReferenceException(
$"Property {method.Name[4..]} of class {method.DeclaringType} is expected to be non-null, but is actually null.");
} where the |
Performance wise, only failure handling is impacted by reflection. So it should be OK. Especially errors like this should be fixed instead of tolerated, so most likely it should only happen once in each run and should be in common in developing time. |
As suggested by this answer, Since I don't feel file path is a reasonable solution, we will stick to |
This change conflicts with Json.net. Fixed in 9ade493 In general, I think this is solved. |
Potentially a solution for dotnet/runtime#3858, which may only work for non-null properties, based on source generators.
It's still quite useful, given that most of the
NullReferenceException
s come from the following sources (with the exception of class's fields)While new unexpected
null
s come from 3 if all obvious warnings are fixed (e.g. returning nullable value in a method). While you can always initialize a non-null property to a value, it is not always reasonable to do so (e.g. inializing a string property to "" while it actually has to be specified by client code).One suggestion from Microsoft is to initialize the property to
null!
. However, in this case, checker and compiler won't do anything, and runtime won't do anything either, even when the null value is obtained. Instead it only produces uselessNullReferenceException: Object reference not set to an instance of an object
error when its members are used.The option is to declare the property's getter as
=> _backField ?? throw new InvalidOperationException("Uninitialized property: " + nameof(ShippingAddress))
, which is much nicer, but needs quite some boilerplate than the option above.This issue will try to use Source Generator to provide a clean solution for any property.
For any non-null property,
It will produce the property as
This error message is already much more helpful than nothing (the current state). Normally there won't two accesses of the same property of different objects in the same line. Two caveats exist though,
InvalidOperationException
is more reasonable?). The code may not fail for NullReferenceException if without the added check. However, when the exception is thrown here, it's definitely a codesmell and should be fixed.So with this feature, it works something like lateinit in Kotlin or late in Dart.
Potential improvements include
The text was updated successfully, but these errors were encountered: