Link to home
Create AccountLog in
Avatar of CipherIS
CipherISFlag for United States of America

asked on

C# Check Variable == Null

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?
Avatar of Shaun Vermaak
Shaun Vermaak
Flag of Australia image

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

Avatar of Arana (G.P.)
Arana (G.P.)

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

you will have to create/declare your idx as nullable
Avatar of CipherIS

ASKER

@aarana - I'm using Var.  Also, I tried delcaring int? idx but I still get the same issue.
@Shaun - that doesn't work

Error      6      Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,int>' and '<null>'
ASKER CERTIFIED SOLUTION
Avatar of Fernando Soto
Fernando Soto
Flag of United States of America image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
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.
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

*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 -User generated image-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 - 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

User generated image
I would recommend to test the dictNames2Indexes  content
Thanks for the all the info.

@Fernando - that is the solution I came up with also.
Not a problem CipherIS, glad to help.
Just be careful, since "0" might be a legitimate value.