13

I'm designing a C#/NHibernate website that features a private messaging system. I would like admins to check if and when a message has been read by a user, and together highlight those messages that haven't been read yet by users. To achieve both, I found two options:

Option 1

class Message
{
    DateTime? Read;
}

where Read==null means not read yet

Option 2

class Message
{
    DateTime Read;
}

where Read==default(DateTime) (January 1st 1 A.D., 0:00:00) means not read yet.

At university, I have been taught to use the NULL value to handle all special cases, and also using the nullable type seems a good choice since it looks easier to query for unread messages by checking whether they are NULL or not.

But, using nullable types at least involves boxing and unboxing in code, with performance decreasing. On the other hand, querying for unread messages means comparing the value (but it can be indexed)

My question is

What is your suggested approach for this? What would best practices suggest in this case?

Community
  • 1
  • 1
usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305

6 Answers6

17

Use DateTime?. Its specific purpose is to avoid using reserved values (aka "magic numbers") to represent special cases, such as null.

Also, using a nullable type introduces no boxing itself. Any values that would have been boxed still will be, but you won't introduce any boxing simply by switching. The Nullable<T> type is actually a struct, and the ability to compare with null (or Nothing in VB.NET) is strictly a language convention. Under the covers, it gets translated into a check on the HasValue property.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
7

Using nullable types does not significantly "decrease performance" compared to alternative approaches. Both DateTime and DateTime? are structs and there is no boxing involved here. Using a nullable is the right choice.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
3

Ask yourself the same question when dealing with numeric values. Do I use 0? What if 0 has real meaning? NULL is the absense of value. 99 times out of 100, go with what makes the intent of the code the most apparent. As far as performance, even if there is one, it will pale in comparison to any performance issues your own code has.

Rich
  • 2,076
  • 1
  • 15
  • 16
0

As Mark and Adam said, a nullable type is the way to go. You won't see a performance hit and it will make querying much simpler.

0

To piggy back on what Mark said, if you had a concern with using a nullable type in the client code, you could set it to the DateTime.MinValue and then in a buisiness layer, switch to a nullable. Then you can feed the nullable type to your DataAccess. This helps to keep a layer of abstarction for using nullables.

jonnyb
  • 352
  • 1
  • 2
  • 17
0

Am I missing a trick?

class Message
{
    DateTime LastRead;
    bool Read;
}
Brett
  • 1,540
  • 9
  • 13
  • 3
    This is semantically equivalent to using `Nullable`, though this version will not be able to take advantage of stack allocation (which `Nullable` can, assuming other conditions are right) and it doesn't encapsulate the logic into a single member. – Adam Robinson Jan 27 '11 at 20:56
  • @Adam Robinson, Agree for the most part. Didn't think using a single member was a pre-requisite of the question (though I guess it's implied), and by not encapsulating two pieces of information in the one field, it does make the structure easier to support - especially if you're the dev who gets to inherit the code :) I don't follow the stack allocation point though - can you expand on this? – Brett Jan 27 '11 at 21:05
  • It was not a prerequisite, but it's easier to perform error-less CRUD operations on one member rather than two. And I didn't know about stack allocation: the point is that I thought that nullable values were allocated into heap! And, anyway, my question was focused on the best practice – usr-local-ΕΨΗΕΛΩΝ Jan 27 '11 at 21:07
  • 1
    @Brett: It's not *especially* important in this particular circumstance, this we're already talking about a member of a class. If that's what you were getting at, then feel free to ignore what I'm about to say. The `Nullable` type is a `struct`, not a `class`, which makes it a value type. The practical meaning of this is that a) all assignment operations perform an actual shallow copy of the object, not a reference copy, and b) When declared as a local variable (and not involved in things like closures), the variable itself lives in the function call's stack (like other value types – Adam Robinson Jan 27 '11 at 21:16
  • 1
    (cont.) like `int`, `DateTime`, `float`, etc.). All objects on the stack are immediately cleaned up at the completion of the function call, rather than sitting on the heap and waiting to be garbage collected. Like I said, since we're talking not about a local variable but a class member, that benefit is irrelevant, but in practice it *is* an advantage to the `Nullable` type versus using a custom construct to represent the same thing. – Adam Robinson Jan 27 '11 at 21:17
  • @Adam Robinson: OK, yep, with you 100% now. :) – Brett Jan 27 '11 at 21:31