Avatar of CipherIS
CipherIS
Flag 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?
.NET ProgrammingC#

Avatar of undefined
Last Comment
gr8gonzo

8/22/2022 - Mon
Shaun Vermaak

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

Arana (G.P.)

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

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

ASKER
@aarana - I'm using Var.  Also, I tried delcaring int? idx but I still get the same issue.
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
CipherIS

ASKER
@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
Fernando Soto

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
gr8gonzo

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 Yamashita

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

⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
it_saige

*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-
Kelvin McDaniel

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 Vermaak

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

Open in new window

Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
Alfredo Luis Torres Serrano

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

intNullableDefault.png
I would recommend to test the dictNames2Indexes  content
CipherIS

ASKER
Thanks for the all the info.

@Fernando - that is the solution I came up with also.
Fernando Soto

Not a problem CipherIS, glad to help.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
gr8gonzo

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