'int' never equal to null for 'char' variables

Can somebody answer me this - why do I get this warning "The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'"

Here is the code

    private char classLetter;
    public char ClassLetter
    {
        get { return classLetter; }
        set 
        {
            if (classLetter == null)
            {
                classLetter = value;
                RaisePropertyChanged("ClassLetter");
            }
            else throw new ArgumentOutOfRangeException();
        }
    }

If I use this code no warning comes in

    private char classLetter;
    public char ClassLetter
    {
        get { return classLetter; }
        set 
        {
            if (classLetter.ToString() == null)
            {
                classLetter = value;
                RaisePropertyChanged("ClassLetter");
            }
            else throw new ArgumentOutOfRangeException();
        }
    }

In short my question is this

How can a int warning be given for a char variable?

EDIT: The 'char' should hold any latin or Cyrillic Letter, no special symbols and no numbers allowed. How should that be filtered?

Answers


In the first case, classLetter is of type char, which can never be null because it's a value type; it would need to be of type char?. So comparing classLetter == null doesn't make sense, as the compiler says.

In the second case, imagine classLetter is 'x'. When doing classLetter.ToString() you get "x", which can be compared with null because it is now a reference type. But again, this is not what you want because classLetter.ToString() will never be null.

If what you want is allowing to set the value only once, you can do this:

private char? classLetter = null; // make it nullable
public char ClassLetter
{
    get {
        if(classLetter == null) { // if we don't have a value yet
            // do something, like throwing an exception
        }else{ // if we do have a value already
            return classLetter.Value; // here we return char, not char?
        }
    }
    set 
    {
        if (classLetter == null) {
            classLetter = value;
            RaisePropertyChanged("ClassLetter");
        }
        else throw new ArgumentOutOfRangeException();
    }
}

A variable of type char is a value variable, not a reference variable, so it can never be null. Only variables of reference types can be null.


You get a warning because a value type will never be null. Full(er) explanation below.

A char in C# is a value type, and can never be null. Consider the purpose of char, that is to represent a Unicode character. There isn't really a good value to pick to mean null in the 0x0 to 0xFFFFFFFF range. (It's true that UTF-16 doesn't actually encompass this entire range, but it might someday).

It's a bit easy to understand with something that has a much smaller range. Consider a byte. It has a range of 0 - 255. Now, in order to represent null, we would need to pick a manner in which null could be stored. A few solutions end up coming up:

  • Store information extra to the byte to represent null. Nullable<Byte> does this.
  • Pick a value in the valid byte range to instead actually mean null. You end up needing to check for this value throughout an entire program. On a small scale, this is a non-issue, but when you don't know who's going to be using your code (a likely scenario), it's something you have to document and hope they see.

It's a much simpler implementation to provided non-nullable value types and a default and provide a wrapper around value types that can represent null if needed. .NET does exactly this with the Nullable<T> type and it's shortcuts with language support (int?,byte?, etc.).

To address your code, if classLetter really could be any value a char can represent, then you can use:

private char classLetter;
public char ClassLetter
{
    get { return classLetter; }
    set 
    {
        classLetter = value;
        RaisePropertyChanged("ClassLetter");
    }
}

You can do this because the compiler will give the user a compile-time error if they try to do something to classLetter they couldn't possibly do sanely.

If you have additional requirement about what classLetter is supposed to represent, update your question and I'll update my answer.

In reponse to your comment on another answer: "I want the behavior to be simple. I want to be able to put any symbol, but I don't want the variable to be left empty"

The only other thing we need to settle on is what "empty" really means. What does empty mean in your context? Once you answer that, you can use default initialization in the type containing classLetter to set it to that value. A generalized example:

class Foo {
  private char classLetter;
  public char ClassLetter {
    get { return classLetter; }
    set {
      classLetter = value;
    }
  }

  public Foo(char classLetter = 'A') {
    this.ClassLetter = classLetter;
  }
}

When a caller uses new Foo(), they will get a Foo instance where Foo.ClassLetter == 'A'. Of course, A can be any sensible default that meets your specific requirements.


Is this what you want?

private char? classLetter;
public char ClassLetter
{
    get { return classLetter.Value; // this will throw exception, if value hasn't set }
    set 
    {
        if (classLetter != null)
        {
            throw new InvalidOperationException("ClassLetter has been set already.");
        }

        classLetter = value;
        RaisePropertyChanged("ClassLetter");
    }
}

Note, that ArgumentOutOfRangeException doesn't fit this case. You don't make any tests for the range of argument.


Need Your Help

Boost multi-index with slow insertion performance

c++ boost

I have the following code (which largely follows the first example here: http://www.boost.org/doc/libs/1_42_0/libs/multi_index/doc/examples.html)). For some reason, with only 10000 insertations to ...