一个关于可空布尔类型的奇怪的事情,c#中的And和Or

本文关键字:中的 Or And 一个 于可空 类型 布尔 | 更新日期: 2023-09-27 18:11:45

我对以下代码有一个奇怪的问题:

!isActive.HasValue || (isActive.HasValue && x.IsActive == isActive.Value)

isActivebool?类型,所以如果isActivenull,那么我将得到以下错误:

可空对象必须有一个值。

你知道吗?谢谢你的帮助!

更新1:

public static List<User> Select(int userId, bool? isActive = null)
{
    var dl = DataLayer.GetDataContext();
    return dl.Users.Where(x => x.ID == userId
                && (!isActive.HasValue || (isActive.HasValue && x.IsActive == isActive.Value))).ToList();
}

这是我得到这个错误的样本函数。我知道这很奇怪!如果我像下面这样修改,它就可以工作了:

!isActive.HasValue || (isActive.HasValue && x.IsActive == isActive)

我明白这个错误的意思,但是我不明白为什么。我想应该能行。所以我把它叫做奇怪的东西!

更新2:

  1. x。IsActive是bool类型,不是bool?我很确定:)
  2. DataLayer.GetDataContext()用于Linq to SQL。

是否可能是因为我使用SQL Server?

感谢大家的帮助

一个关于可空布尔类型的奇怪的事情,c#中的And和Or

由于它是Linq-to-SQL,那么它可能不会短路或以正确的顺序计算表达式部分。我没有使用过它,但从我的理解它读取你的lambda表达式的内容,并将其转换为一个接近等效的SQL语句;它实际上并不像c#代码那样执行

即便如此,当值固定时,对每个项进行检查似乎有点多。您可以这样重写该方法:

public static List<User> Select(int userId, bool? isActive = null)
{
    var dl = DataLayer.GetDataContext();
    var users = dl.Users.Where(x => x.ID == userID);
    if (!isActive.HasValue)
    {
        return users.ToList();
    }
    else
    {
        bool isActiveValue = isActive.Value;
        return users.Where(x => x.IsActive == isActiveValue).ToList();
    }
}

这样,如果生成的SQL查询不适用,就不会有额外的约束。编辑:更新了它,所以除此之外,没有可空的布尔值被传递到你的查询。

您的代码中有两个不同的isActive值,因此看起来您在第二次检查时遗漏了x:

!isActive.HasValue || (x.isActive.HasValue && (x.IsActive == isActive.Value))
                   ----^

我还在相等性检查周围添加了括号,以消除顺序或操作的混淆

哦,这是因为LINQ中的查询生成器。可能有一种方法可以在一个查询中执行您的意图(实际上,它听起来像您在q中更新的代码就是这样做的),但是要做的直接的事情是将其拆分为两个查询,因为您事先知道isActive的值。这样,您就不必担心查询生成器可能会做什么或可能不会做什么。

对于这个问题,您不妨将这两个函数完全分开,只是为了使将来的维护者更难以意外地落入相同的陷阱:

public static List<User> Select(int userId)
{
    return DataLayer.GetDataContext().Users.Where(x => x.ID == userId).ToList();
}
public static List<User> Select(int userId, bool isActive)
{
    return DataLayer.GetDataContext().Users.Where(x => x.ID == userId && x.IsActive == isActive).ToList();
}

你的代码不应该在那一行失败。

我猜它在你的if块的某个地方失败了(我假设这就是这个语句的目的)。如果isActivenull,第一个条件将返回true。如果你尝试在块内使用isActive,你会得到Nullable错误。

如果这样做,代码在哪里失败?

public static List<User> Select(int userId, bool? isActive = null)
{
    var dl = DataLayer.GetDataContext();
    return dl.Users.Where(x => E(userId, isActive, x)).ToList();
}
static bool E(int userId, bool? isActive, User x)
{
    return x.ID == userId && D(isActive, x);
}
static bool D(bool? isActive, User x)
{
    return (!isActive.HasValue || C(isActive, x));
}
static bool C(bool? isActive, User x)
{
    return (isActive.HasValue && B(isActive, x));
}
static bool B(bool? isActive, User x)
{
    return x.IsActive == isActive.Value;
}

将其类型从bool改为bool?

bool? IsActive = false;