C# Check Variable == Null

CipherIS
CipherIS used Ask the Experts™
on
I have the below code.
var idx = dictNames2Indexes.FirstOrDefault(x => x.Key.Contains(value)).Value;

if (idx != null)
   ......

Open in new window

I am receiving "The result of the expression is always 'true' since a value of type 'int' is never equal to 'null' of type 'int?'"

Shouldn't above check for nulls?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Shaun VermaakSenior Consultant
Awarded 2017
Distinguished Expert 2018

Commented:
Check for null on
dictNames2Indexes.FirstOrDefault(x => x.Key.Contains(value))

Open in new window

not on
dictNames2Indexes.FirstOrDefault(x => x.Key.Contains(value)).Value

Open in new window

int is a value type, value types cant be nulled.

you will have to create/declare your idx as nullable

Author

Commented:
@aarana - I'm using Var.  Also, I tried delcaring int? idx but I still get the same issue.

Author

Commented:
@Shaun - that doesn't work

Error      6      Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,int>' and '<null>'
Retired
Distinguished Expert 2017
Commented:
Hi CipherIS;

It looks like dictNames2Indexes is a Dictionary<string, int>, if so your statement
var idx = dictNames2Indexes.FirstOrDefault(x => x.Key.Contains(value)).Value;

Open in new window

Should return 0 if it could not find the Key and the assigned value if it does find the Key. If that is not happening please post the code where you define dictNames2Indexes in your code.

Commented:
The answer is scatted among the previous comments, so let me try to take a stab at explaining it.

So first off, .NET is correct when it is saying that idx will never be null. The dictName2Indexes is a Dictionary<string,int> which means that all the VALUES of this dictionary are "int" types, and "int" can never be null.

In .NET, there are several value types that can never be null, like int and DateTime and bool.

For example, try running this line of code in .NET:
bool testBoolean = null;

Open in new window


It'll tell you it can't do that because "bool" is a non-nullable type.

So for any of these types of values (int, DateTime, bool), if you don't specify their value, then their default value is not null, but something specific to the type of data they have.
bool testBoolean; // Default value is false
int testInteger; // Default value is 0
DateTime testDateTime; // Default value is DateTime.MinValue

Open in new window


It is very easy to tell .NET to allow them to be null, though. You simply add a ? to the end of the data type:
bool? testBoolean; // Default value is null
int? testInteger; // Default value is null
DateTime? testDateTime; // Default value is null

Open in new window


So by declaring testInteger as an int? type, it can now be null or it can be a number.

So coming back to your dictionary, if your dictionary is a Dictionary<string, int> type, then the FirstOrDefault() call like this:
dictNames2Indexes.FirstOrDefault(...criteria here...)

Open in new window


...will return the first matching KeyValuePair entry for a successful match. If it's unsuccessful, then you get back a KeyValuePair object that contains the default values for string (your dictionary's key type) and int (your dictionary's value type). So the unsuccessful search would return a KeyValuePair of { null, 0 } (string is a nullable value type so you don't need a ? at the end of it).

Then when you add .Value to the end of that, you're pulling the value, which for the unsuccessful search would be 0.

So when you have this line of code after your search:
if (idx != null)

Open in new window

...then .NET is complaining and saying, "I'm either going to have a successful search and return an integer, or I'm going to have an unsuccessful search and return 0, which is an integer. So no matter what, I'm returning an int, which is not nullable."

As to what you can do here, if 0 is a valid number in your scenario (I'm guessing this is related to that Excel spreadsheet thing, so 0 could be a valid column index), then you'd just change your dictionary definition to have that ? after the int:

Dictionary<string,int?> dictNames2Indexes = new ...blah blah...

Now when you have an unsuccessful search in FirstOrDefault, your default KeyValuePair will be {null, null}, so your null check on idx will work as expected.
Edgard YamashitaSystems Analyst

Commented:
If your variable dictNames2Indexes is a dictionary, then you should check for the key before, and if it exists get the value (without the need to check for null if the key exists).

Eg:
            Dictionary<string, int> dictNames2Indexes = new Dictionary<string, int>()
            {
                { "TEST", 1 }
            };

            if(dictNames2Indexes.ContainsKey("TEST"))
            {
                int idx = dictNames2Indexes["TEST"];
            }

Open in new window

Commented:
*NO POINTS*

Others have explained why you are getting your error.  However, since keys in a Dictionary must be unique, you should really be using the TryGetValue method.  Example -
using System;
using System.Collections.Generic;
using System.Linq;

namespace EE_Q29126855
{
	class Program
	{
		static readonly Dictionary<string, int> numbers = (from i in Enumerable.Range(0, 20)
														   select new { Key = $"Key{i}", Value = i }).ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase);
		static void Main(string[] args)
		{
			int value = default(int);
			Console.WriteLine($"Default integer is 0: {value == 0}");
			foreach (var i in Enumerable.Range(15, 6))
			{
				if (numbers.TryGetValue($"Key{i}", out value))
				{
					Console.WriteLine($"Value of Key{i} is {value}");
				}
				else
				{
					Console.WriteLine($"Key{i} not found in dictionary; value is 0: {value == 0}");
				}
			}
			Console.ReadLine();
		}
	}
}

Open in new window

Produces the following output -Capture.PNG-saige-
These are all very informative answers, and each is good for learning. In the spirit of your original question, however, I think you'll want to use the following check:

if (dictNames2Indexes.Any(x => x.Key.Contains(value))
{
	var idx = dictNames2Indexes.FirstOrDefault(x => x.Key.Contains(value)).Value;
	...
}

Open in new window


Note that I assume you don't need idx outside of the if block.

The important thing to remember here is that an initialized Dictionary<T, TV> won't ever have completely null entries. At worst (and as has already been said), it will return an empty KeyValuePair<T, TV> with the default values for T (the key) and TV (the value). In your case the default key value is "" and the default Value for that key is 0.
Shaun VermaakSenior Consultant
Awarded 2017
Distinguished Expert 2018

Commented:
@Shaun - that doesn't work
Probably
dictNames2Indexes.Where(x => x.Key.Contains(value)).FirstOrDefault()

Open in new window

For an int with ? at the end (Nullable) the default value is null

intNullableDefault.png
I would recommend to test the dictNames2Indexes  content

Author

Commented:
Thanks for the all the info.

@Fernando - that is the solution I came up with also.
Fernando SotoRetired
Distinguished Expert 2017

Commented:
Not a problem CipherIS, glad to help.

Commented:
Just be careful, since "0" might be a legitimate value.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial