In new version of C# is default option nullable. I mean this
It's really good feature for me. But I don't understand one thing. Is property nullable by me or by program?
I know that this property should never be null because I fill it in the form and never set it as null. But in general it can be null. How should I mark it?
Example:
UserModel:
public string? Name { get; set; }
public string Name { get; set; } = null!
I'm really confused.
I know that this property should never be null because I fill it in
the form and never set it as null. But in general it can be null. How
should I mark it?
If it is set to non-null in the constructor, then you can declare it a string rather than string? because it really is guaranteed to never be null.
If, however, it is null after the constructor has run (and before any other methods are called) then it CAN be null and you should declare it as string?.
The dichotomy you face is that (from what you say) it should never be null when the class is used correctly - i.e. if the calling code does indeed call the method that initialises the property to non-null before accessing it, it will be guaranteed to be not null - which means you would like to declare it as a non-null string so that callers don't need to use the null-forgiving ! operator to suppress nullable warnings.
What to do in this case? Well I think you have three possibilities:
Declare the property as nullable: string? and let the callers suppress the nullable warning or check the value for null.
Initialise the property to a non-null default such as string.Empty and declare the property as non nullable string.
Assume that accessing the property when it is null is a programming error, and throw an appropriate exception.
For the last case, you could do something like this:
private string? _name;
public string Name
{
get
{
if (_name == null)
throw new InvalidOperationException("Do not access Name without setting it to non-null or calling Init()");
return _name;
}
set
{
if (value == null)
throw new ArgumentNullException("value", "You cannot set Name to null.");
_name = value;
}
}
This assumes that there's a method called Init() that can be called to initialise Name, but of course your code may vary.
Regardless of which of those approaches you take, you should really check for null in the property setter, as shown above.
Note that a more succinct way of writing the setter null check is:
set
{
_name = value ?? throw new ArgumentNullException("value", "You cannot set Name to null.");
}
Where a value can be null, and the <Nullable>enable</Nullable> value is set in your *.csproj file, you must declare those values as nullable.
In your given example, the nullable type (instead of assigning null as a default) is the way to go:
public string? Name {get; set;}
You'll also notice VS complaining at you with warnings every time you introduce a property etc which can be nullable, but isn't marked as such.
Related
Why do I not get a non-nullable warning (or some other warning) for FieldTwo not being set in the constructor? I'm using .Net 5.0 and Nullable option is set to true in the project file.
public class MyClass
{
public string FieldOne;
public long FieldTwo;
public MyClass (string p1, long p2)
{
FieldOne = p1;
// FieldTwo is not set. Where is the non-nullable warning!?
}
}
long is a value type and cannot be null. So there is no reason for warn about null. What I suspect you want is a field not initialized warning.
If a private field is never assigned, it causes a CS0649 warning. However, if the field public, or is modified anywhere in the class, this warning is not raised. For example, the following would be perfectly acceptable code, and should not give a warning:
public class MyClass{
private long myLong;
public void Increment() => myLong++;
}
If the field should only be set from the constructor you should declare it as readonly. However, public readonly fields does not trigger a warning, even if they probably should. See Why do I NOT get warnings about uninitialized readonly fields?.
long is a value type and can't be null. Value types are always initialized when declared with a default value, in this case 0. You'd have to change the field type to long? if you wanted to store a null
Those fields aren't properties anyway. This isn't just semantics. Fields are implementation details, even public fields. They aren't considered part of a class's API surface. Properties are part of the API, they get serialized by default by all serializers (fields don't) and guarantee In fact having public fields is a code smell.
To ensure all properties are initialized you can use a record type instead of a class :
public record(string PropertyOne, long PropertyTwo);
This generates a class with init-only properties for PropertyOne and PropertyTwo and a constructor that requires values for all properties. Along with equality operators, deconstructors and a ToString() implementation that returns all properties in the form of a JSON string.
I have a question regarding nullable reference type system available since C# 8.
Suppose we have a C# domain model class with a mutable reference type property like below:
public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
So far no problem. But consider real world scenario, I often want to check the validity of the property as it's a public mutable property and I have to make sure the model invariant whenever property is changed.
public class Person
{
private string _name;
public string Name
{
get => _name;
set => _name =
value ?? throw new ArgumentNullException("Name is required.");
}
public Person(string name)
{
Name = name;
}
}
Then compiler generates CS8618 warning, basically saying:
Non nullable field _name is not initialized. Consider declare the
field as nullable type.
So every time I encounter the warning I have to enclose the constructor with the following pragma directive.
#pragma warning disable CS8618
public Person(string name)
{
Name = name;
}
#pragma warning restore CS8618
But I think it's redundant and tedious to do it always. Am I misusing something or is there better way to write such property without warning?
Of course I can change the property type to string? as compiler suggests but notionally it's not acceptable as a solution as Person should always have non null name and we want to explicit about such invariant condition in domain class.
Another solution I considered is to drop the argument validation logic and just relying on the nullable compiler warning, but it's not always possible (I mean often validation other than null check is also required.), it's just warning anyway in regular project settings, so I don't think it's a good solution.
For now you can avoid this warning by initializing a _name field using default value with null-forgiving operator !, like
private string _name = default!;
or
private string _name = null!;
There is also an open GitHub issue for that.
You can also declare the _name as string? and specify that return value of Name property can't be null (even if string? type allows it), using NotNull attribute
private string? _name;
[NotNull]
public string? Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException("Name is required.");
}
It should be fine, otherwise compiler shows you a warning before validation logic will take place in a setter
set => _name = value ?? throw new ArgumentNullException("Name is required.");
Consider the following code
var person = new Person(null);
In this case you'll get
warning CS8625: Cannot convert null literal to non-nullable reference
type.
before ArgumentNullException will be thrown.
If you set <TreatWarningsAsErrors>true</TreatWarningsAsErrors> or treat CS8625 warning as error, your exception won't be thrown
You can disable the rule by creating an .editorconfig file (with the attached code) in the root of your project. It does not solve it but it will no longer show the warning
[*.cs]
# CS8618: Non nullable field _name is not initialized. Consider declare the field as nullable type
dotnet_diagnostic.CS8618.severity = none
You can now apply MemberNotNull attribute on the setter to let the C# compiler know that non-nullability condition for the _name field is being maintained by that method.
C# language reference
using System.Diagnostics.CodeAnalysis;
public class Person
{
private string _name;
public string Name
{
get => _name;
[MemberNotNull(nameof(_name))]
set => _name = value ?? throw new ArgumentNullException("Name is required.");
}
public Person(string name)
{
Name = name;
}
}
Based on this:
Warnings for initialized fields
Q: Why are warnings reported for fields that are initialized indirectly by the constructor, or outside the constructor?
A: The compiler recognizes fields assigned explicitly in the current
constructor only, and warns for other fields declared as non-nullable.
That ignores other ways fields may be initialized such as factory
methods, helper methods, property setters, and object initializers. We
will investigate recognizing common initialization patterns to avoid
unnecessary warnings.
So with that being said, for now, moving the assignment directly into the constructor, is the only possible way. And for sure, using the pragma directive seems fine for this IMO.
I'd like to further expand Pavel Anikhouski's answer by pointing out that using null-forgiving operator is considered a proper practice according to MS C# language reference.
You can also use the null-forgiving operator when you definitely know that an expression cannot be null but the compiler doesn't manage to recognize that.
To disable this warning and other null-related warnings for the whole Project, right click on your project -> Properties. Under the section Build -> General, set Nullable to Disable.
Tested on Visual Studio version 17
I have a very simple class Address:
public class Address
{
public string Name { get; set; }
public string Country { get; set; }
}
I declared Name as string and NOT as string?, because it should never be null.
Now i have a method where i get an instance of this class as parameter:
public List<Address> SearchAddress(Address search)
{
...
if (!search.Name.Equals(string.Empty))
{
temp = query.Where(a => a.Name.Contains(search.Name));
}
...
}
Now my colleague called this method and i got a System.ArgumentNullException because search.Name is null.
If i have a non-nullable int property i get 0 as value if i don't assign anythig to it.
Why is a non-nullable string property null if no value is assigned and not just string.Empty?
int is a non-nullable value type (i.e., a struct). Value type fields will be initialized to their default value. E.g., int to 0, bool to false, etc. Full list here.
When you want to be able to assign null to value type variables, you can use Nullable<T> where T : struct or, in the case of an int, Nullable<int>/int?.
string, on the other hand, is a reference type. Reference types can be null, and reference type fields will be initialized as null. As such, having a Nullable<string>/string? is not allowed nor would it make sense.
In your case, you'll want to check whether the Name string is null or empty
if (! String.IsNullOrEmpty(search.Name)) { ... }
String can hold null values, its simple as that!
String is a reference type, but is has certain behavior of value type.
Please read this topic : Null strings and empty strings
You can use the string.IsNullOrEmpty to determine a instance if it is Null and empty string .
string is a reference type and it's null by default. String.empty is equivalent to a double quoted empty string ("") and hence null is not the same as String.empty.
This question already has answers here:
Why type "int" is never equal to 'null'?
(8 answers)
Closed 9 years ago.
I am trying to compare the following:
if (e.CreatedBy == null)
But this is giving me an error saying:
The result of the expression is always 'false' since a value of type
'int' is never equal to 'null' of type 'int?'
Can someone help tell me how I can check if the value of CreatedBy has not been set yet? Note here's the definition of CreatedBy:
public int CreatedBy { get; set; }
int is a value type which means it can never be null. Its default value is 0. If you want store null instead of 0, you need to change the type to int?, which is a nullable int type. To read more about Nullable types, check "Nullable Types (C#)"
The type of CreatedBy is int which is a type that cannot take the value null.
From what you currently have, it is not possible to detect whether or not CreatedBy has been set. Suppose that its default value is 0. Then the value is set to 1, and then back to 0. Now, how can you distinguish the current 0 from the original unmodified 0?
If what you want to do is detect whether or not the value is 0, well I don't think I need to tell you how to do that. If you really want to detect whether or not the value has ever been set you'll need to maintain a bool flag and set that flag the first time the property's setter executes.
The int value is a type of non Nullable types see
http://msdn.microsoft.com/en-us/library/1t3y8s4s(v=vs.80).aspx
if you want to check if set or not use
//GetValueOrDefault()
private int? createdBy;
public int CreatedBy
{
get { return createdBy.GetValueOrDefault(-1); }
set { createdBy= value; }
}
///your code
if(e.CreatedBy == -1)
{
//not set
}else
{
//set
}
int is a value type which can't be null. If you want to make a value type nullable, you need to use int? which is a shorthand way of creating a Nullable object like this
Nullable<int> myNullable = new Nullable<int>();
Can someone help tell me how I can check if the value of CreatedBy has not been set yet?
No, that is impossible since value types (like int) can't be null.
But you can use Nullable Types which let's you declare it as a "Nullable of int" and then it can be null.
From the documentation I linked to:
A nullable type can represent the correct range of values for its underlying value type, plus an additional null value
To make your property a "Nullable of int", just add a questionmark:
public int? CreatedBy { get; set; }
The above syntax is actually just a shorthand for:
public Nullable<int> CreatedBy { get; set; }
Because default value of int is 0 not null. e.CreatedBy never going to have the value like null so better is to compare it with 0.
if (e.CreatedBy == 0)
nullable int can be equal to null but not nullable (default int) never going to be equal of null.
Well you are using int not nullable int so it can't be null ever?
You could change the definition of your CreatedBy as follows:
public int? CreatedBy { get; set; }
and they you can make the following check
if (e.CreatedBy == null) or if (e.CreatedBy.HasValue)
Ok, lot's of answers explaining the obvious. Here's a couple of ideas for actually solving your problem:
One alternative: Just use the default value 0.
Now, instead of checking for null in your original code, you can check if(CreatedBy > 0).
If you prefer to be more "explicit", you could use -1 to explicitly indicate "empty" or "not set", by setting it as a default value in the constructor. I'm not saying this is ideal in all situations, but it is fairly simple:
public YourClass(){
CreatedBy = -1;
}
public int CreatedBy { get; set; }
Obviously, this leads to the test: if(CreatedBy != -1).
Another alternative is to use nullable of int, as some have sugested, and check for null. This has the downside of having to deal with the type int? instead of the more "general" int everywhere you work with that value though. That may not be a problem, but sometimes it can get annoying, especially if you pass the value to other methods, classes, or whatever..
To answer your question, you can't check whether the value has been set, you can only check what value it has now. Any property will have a 'default' value when it's initialised. For most types (including Nullable), the default value is null, however for int the default value is 0.
If you're sure that nobody would ever set this property to 0 for any other reason, then you can test for:
if (e.CreatedBy == 0)
...however, if 0 is a valid value for this property under another circumstance, then testing for 0 won't help you. Equally, it you change the property to nullable, then you can test for:
if (e.CreatedBy == null)
...as per your original design, but if there is any other reason why the property might be set to null then again you're not finding out if it had ever been set, only that the last time it was set, it was set to null.
If you want to really know whether the value has been set, you need to arrange for something else to happen when the setter is used, for example to set another flag. Here's an example of doing exactly this, where the CreatedByHasBeenSet property is false (the default value of bool!) until the setter on CreatedBy is called:
private int createdBy;
public int CreatedBy
{
get
{
return this.createdBy;
}
set
{
this.createdBy = value;
this.CreatedByHasBeenSet = true;
}
}
public bool CreatedByHasBeenSet { get; private set; }
I'm facing a strange behaviour of one of my properties in my MonoTouch-Application.
It seems that the given value can not be returned by the Property, but the Debugger in MonoDevelop says that the private field in the background actually holds the value??
This is how the property got implemented:
public int? Status
{
get { return this._status; }
internal set
{
if (this._status != value)
{
// Status hat sich geändert, darum Speichervorgang nötig
this._mustBeSaved = true;
this._status = value;
this.NotifyPropertyChanged("Status");
}
}
}
And here you can see what the Debugger says:
How is it possible that the reflected value ("value") holds the correct value, and the reflected field "has_value" says that it's not holding any value at once? For this reason it's not possible to get the value from the property.
(The only difference between this and my other properties is that this property is nullable)
_value is actually null. What you see in the debugger are internal fields and shouldn't be relied upon.
The compiler probably reuses the storage when you set the field to null, so that's why you're still seeing an old value in the internal value field. What counts are the public properties HasValue and Value.