Can you shorten multiple chained null checks in C#? [duplicate] - c#

This question already has answers here:
C# null check chain in method call
(4 answers)
Closed 2 years ago.
I have just started learning C#. I'm making a game mod for Rimworld and cannot modify the pawn code. Apparently, any of the chained objects may be null. Is there a better way to bail out of the method than what I've done?
Thank you.
private void cleanseParadoxicalMemories(Pawn pawn, Dictionary<string, string> knownPawnIDs)
{
if (pawn.needs == null || pawn.needs.mood == null || pawn.needs.mood.thoughts == null || pawn.needs.mood.thoughts.memories == null)
{
return;
}
// Remove any crazy-making memories from a now-invalid timeline due to traveling across an Einstein-Rosen bridge
// (basically, selective amnesia about everyone not going with us.)
foreach (var paradox in pawn.needs.mood.thoughts.memories.Memories.ToList())
{
if (paradox.otherPawn != null)
{
pawn.needs.mood.thoughts.memories.RemoveMemory(paradox);
}
}
}

You can use null conditional operator ?. to achieve this :
private void cleanseParadoxicalMemories(Pawn pawn, Dictionary<string, string> knownPawnIDs)
{
if (pawn?.needs?.mood?.thoughts?.memories == null)
{
return;
}
// Remove any crazy-making memories from a now-invalid timeline due to traveling across an Einstein-Rosen bridge
// (basically, selective amnesia about everyone not going with us.)
foreach (var paradox in pawn.needs.mood.thoughts.memories.Memories.ToList())
{
if (paradox.otherPawn != null)
{
pawn.needs.mood.thoughts.memories.RemoveMemory(paradox);
}
}

Related

Can a C# Safe Navigation Operator (?.) be used to check if Object is null, as well as property?

This is mainly a syntactic sugar/best-practice question.
Here is a code example in question:
if (_instanceData?.DataSourceType != null && Util.IsEntityBacked(_instanceData.DataSourceType) {// code}
I understand that the safe navigation operator will continue execution if _instanceData is null, but in this case will the first boolean in the conditional be evaluated as expected? Is this going to successfully null check on both _instanceData and DataSourceType?
Another Example:
if (LockAcquired && _instanceData?.ObjInfo != null) {// code}
In this case, it is possible that _instanceData is null, or it is not, but ObjInfo is null. Is it better practice to just old-fashioned null check both object and property, or will this get the job done as expected?
edit: The question is better described as:
Is if (obj?.prop != null) equivalent to
if (obj != null && obj.prop != null)
The first is equivalent to
if (_instanceData != null && _instanceData.DataSourceType != null && Util.IsEntityBacked(_instanceData.DataSourceType) {// code}
The second is equivalent to
if (LockAcquired && _instanceData != null && _instanceData.ObjInfo != null) {// code}
So it will check if _instanceData is null, then check if _instanceData.DataSourceType is null, then the last condition. As you said, this is just syntactical sugar so you don't have to write two != null conditions. The IL code that results is exactly the same, so its a matter of preference whether or not to use the operator.
It does save a TON of space when accessing deeply nested properties, which is where its most useful
if(Parent != null)
{
if(Parent.Child != null)
{
if(Parent.Child.GrandChild != null)
{
Parent.Child.GrandChild.GreatGrandChild.name = "Parent IV";
}
}
}
becomes
Parent?.Child?.GrandChild?.GreatGrandChild.name = "Parent IV";
Saves a lot of space! Even if you collapsed all the ifs into one statement it still saves loads of keystrokes and screen noise.

Problems checking for null in C# [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I have the following method:
void setTexts()
{
if (queueIn != null)
{
queueIn.text = countIn.ToString();
}
if (queueOut != null)
{
queueOut.text = waitingForPickup.ToString();
}
}
I want it to do nothing if queueIn is null, but I keep getting a null reference exception saying queueIn is null. Why is it going into the if block when queueIn is null?
EDIT: the problem disappeared when I added a Debug.Log check, so it probably hadn't saved the previous dozen times or something. Thanks for your suggestions! I'm pretty new to C#.
You need to check all object deference points. In this case, countIn could be your offender.
Here's a possible solution to remove your exception.
void setTexts(){
if (queueIn != null && countIn != null) {
queueIn.text = countIn.ToString ();
}
if (queueOut != null && waitingForPickup != null){
queueOut.text = waitingForPickup.ToString();
}
}
You are calling ToString() on countIn and waitingForPickup - you need to check them too. E.g.:
void setTexts(){
if (queueIn != null && countIn != null) {
queueIn.text = countIn.ToString();
}
if (queueOut != null && waitingForPickup != null) {
queueOut.text = waitingForPickup.ToString();
}
}

Checking if dropdowns are null or not [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 8 years ago.
This falls over on the first elseif. Basically I have a load of dropdowns and I'm trying to work out which filters to select based on if people have selected the dropdown items or not.
An unhandled exception of type 'System.NullReferenceException' occurred in
Based On
private void btnSearch_Click(object sender, EventArgs e)
{
if (ddCompany.SelectedItem.ToString() == null && ddStatus.SelectedItem.ToString() == null)
{
UpdateTicketsList("NO", "NO");
}
else if (ddCompany.SelectedItem.ToString() != null && ddStatus.SelectedItem.ToString() == null)
{
UpdateTicketsList(ddCompany.SelectedItem.ToString(), "NO");
}
else if (ddCompany.SelectedItem.ToString() == null && ddStatus.SelectedItem.ToString() != null)
{
UpdateTicketsList("NO", ddStatus.SelectedItem.ToString());
}
}
Calling ToString on a null object will result in a NullReferenceException.
Remove the calls to ToString in your if-statement expression and your code should work as expected :)

possible null assignment in Dictionary Contains Key

Getting a warning on "!redirectsDictionary.ContainsKey(autoRedirect.Key)"
asp.net possible null assignment to entity marked with "notnull" attribute.
Just wondering what that's about ?
private static readonly Dictionary<string, Redirect> AutoRedirectsDictionary = new Dictionary<string, Redirect>();
foreach (var r in db.SelectItems("fast:/sitecore/content/Redirects Root//*[##templatename='Auto Redirect']"))
{
GenerateRedirects(Context.Database.GetItem(r.Fields["Root Node"].Value), r["URL Prefix"]);
foreach (var autoRedirect in AutoRedirectsDictionary)
{
if (!string.IsNullOrEmpty(autoRedirect.Key) & !redirectsDictionary.ContainsKey(autoRedirect.Key))
{
//Add to dictionary
redirectsDictionary.Add(autoRedirect.Key, autoRedirect.Value);
}
}
}
public static void GenerateRedirects(Item redirectFolder, string urlPrefix)
{
if (redirectFolder == null)
return;
var childList = redirectFolder.GetChildren();
foreach (Item child in childList)
{
if (Utilities.HasFieldValue(child, FieldToFind))
{
var shortcutUrl = urlPrefix + child.Fields[FieldToFind].Value.ToLower();
if (!string.IsNullOrEmpty(shortcutUrl) && !AutoRedirectsDictionary.ContainsKey(shortcutUrl))
{
AutoRedirectsDictionary.Add(shortcutUrl,
new Redirect(String.Empty, child, true));
}
}
else
{
GenerateRedirects(child, urlPrefix);
}
}
}
It might have to do with your use of the single & operator. A single & will not use short-circuiting to bypass a statement but will instead choose the path to execute after all expressions have been evaluated. So even though you're checking !string.IsNullOrEmpty(autoRedirect.Key) prior to the ContainsKey call, both expressions will be evaluated first and then the path of execution will be decided.
Edited as I realized I didn't truly answer your specific question (and you may already know this) but !redirectsDictionary.ContainsKey(autoRedirect.Key) will throw an exception if the key is null. Since the datatype for the key is a string there is a possibility it will throw an exception if it is null, hence the warning.

Is there a clever way to handle this null object reference?

So I have a Retrieve() function, which either gets me an object or a null (if that object is not found). I'm using an if statement with a boolean attribute of that object. It's set up like this.
if(Retrieve(index).IsForm == true) {}
The issue with this is that if it doesn't find an object, it'll throw a null reference exception. There are some ways around this, of course, but none that I find concise. There's a try...catch, but that seems pointless when I expect the error. I can check if the object is null first, if(Retrieve(index) != null), but that seems like adding needless nesting. Is there a clever way to handle this? I thought of using the null coalescing operator but it doesn't work in this situation.
You can either call the method twice:
if(Retrieve(index) != null && Retrieve(index).IsForm == true) { }
Or you can break the lines apart and store the result before the if:
var result = Retrieve(index);
if(result != null && result.IsForm == true) { }
You could write an IsForm function to do both operations for you:
bool IsForm(int index)
{
var result = Retrieve(index);
return result != null && result.IsForm;
}
if (IsForm(index))
...
The Null Object pattern would be helpful here. It keeps your calling code clean but does add an additional class.
class NullWhatever : Whatever
{
public NullWhatever() { IsForm = false; }
}
Whatever Retrieve(...)
{
...
return new NullWhatever(); // instead of null
}
You could make a Nullable_IsForm extension method. Then you could check for the null condition.
public static class RetrieveExtension
{
public static bool? Nullable_IsForm(this Retrieve retrieved)
{
if(retrieved == null)
{
return null;
}
else
{
return retrieved.IsForm;
}
}
}
Then in your code you'd check it against bool values
if(Retrieve(index).Nullable_IsForm == true)
{}
else if (Retrieve(index).Nullable_IsForm == false)
{}
else if (Retrieve(index).Nullable_IsForm == null )
{}
I don't think there is any more concise way to do it, no.
Shortest I can think of is:
if(Retrieve(index)!=null && Retrieve(index).IsForm == true) {}
but I don't like this because it calls Retrieve(index) multiple times. I'd go for
var foo = Retrieve(index);
if(foo!=null && foo.IsForm == true) {}
but that is obviously not doing anything clever or more concise. It is probably more efficeint than some of the alternatives.
You could put both conditions in the same if:
if(Retrieve(index)!= null && Retrieve(index).IsForm == true) {}
Thanks to short-circuit, if the null-check fails, rest of the expression is not evaluated.

Categories

Resources