简体   繁体   中英

Using .HasValue() in LinQ when dealing with a Nullable type, is there a way to get rid of “Possible SystemInvalidException”?

I have a list of Foo's .

Foo has a CreateDate of type DateTime?

I want to get the oldest Foo . I tryed with the intuitive way, but I get a possible System.InvalidOperationException .

DateTime oldestFoo = 
(from f in allFoos where f.CreateDate.HasValue select f.CreateDate.Value).Min(); 

Is there something I missed or is it just not possible this way?

I can do it without LinQ this way, but I want to learn the other approach too.

DateTime oldestFoo = DateTime.MaxValue;
foreach (var foo in allFoos)
{
    if (foo.CreateDate.HasValue && oldestFoo > foo.CreateDate)
    {
        oldestFoo = (DateTime)foo.CreateDate;
    }
}

Just in case you're still stuck, you might want to consider using DefaultIfEmpty - see if this helps:

DateTime oldestFoo = allFoos.Where(f => f.CreateDate.HasValue)
                            .Select(f => f.CreateDate.Value)
                            .DefaultIfEmpty(DateTime.MaxValue)
                            .Min();

I don't think you want to use GetValueOrDefault within your query, as otherwise you'll still have a problem if the first query returns no results at all.

You can use the GetValueOrDefault method, which doesn't cause an exception, and as there are no null values at that point, the result is the same:

DateTime oldestFoo = (
  from f in allFoos
  where f.CreateDate.HasValue
  select f.CreateDate.GetValueOrDefault()
).Min();
DateTime oldestFoo =
    (from f in allFoos select f.CreateDate).Min() ?? DateTime.MaxValue;

Or, slightly more succinctly:

DateTime oldestFoo =
    allFoos.Select(f => f.CreateDate).Min() ?? DateTime.MaxValue;

My suggestion is NOT to project DateTime? to DateTime before calling Min .

Here's a Nullable<int> sample that does not project to int to illustrate how the Min and Max extensions behave with empty and different lists of nullable int's.

var foo = Enumerable.Empty<int?>();
Console.WriteLine(foo.Min());

foo = new int? [] { null, -20, 10 };
Console.WriteLine(foo.Min());
Console.WriteLine(foo.Max());

The output of the above is:

null 
-20
10

Instead of filtering the DateTime? instances in the list of Foos to only those that have values, allow the Min extension to work with the DateTime? values instead of projecting to DateTime . When you have an empty list (as in the example above) you'll get a null value from Min instead of the InvalidOperationException .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM