比较不可为 null 的 `int` 和 `null` (LINQ)
Comparing non nullable `int` to `null` (LINQ)
我有 EF 模型 class,我决定用一个 bool
属性:
扩展 class
class A
{
public int Id { get; set; }
public string Value { get; set; }
}
class A_DTO : A
{
public bool BoolProp { get; set; }
}
class C
{
public int Id { get; set; }
public int A_Id { get; set; }
}
然后我写了一个方法,它将 return A
集合与其他 C
集合连接起来,其中包含 A <=> C 映射(好吧,在现实世界中例如它包含一些 SystemId
和 linq 查询将由 2 列连接)和 returns A_DTO
集合:
internal IQueryable<A_DTO> MyMethod() =>
from a in dbContext.A
join c in dbContext.A_C_Mapping
on a.Id equals c.A_Id into g
from cc in gg.DefaultIfEmpty()
select new A_DTO
{
Id = a.Id,
Value = a.Value,
BoolProp = cc.A_Id != null //<- will be always true, well, that what says warning message
}
(dbContext
是我的 EF 上下文对象)
当然因为 cc.A_Id
不是可为空的 int
会出现警告消息,说
"The result of expression will be always 'true'
since the value of type int
is never equal to null
value of type int?
"
这是真的,但实际上我得到的结果完全正确,因为我的左外连接 return null
s 在 C
集合中缺少映射时。
所以问题是:这样做并保持原样是正确的方法,还是我需要以其他方式实施?
如果 cc.A_Id 永远不会变为 null 并且您想设置 BoolProp 的值,那么您可以通过将 select 更改为以下内容来删除警告:
select new A_DTO
{
Id = a.Id,
Value = a.Value,
BoolProp = true
}
Value Types
将始终具有默认值(将它们声明为 Nullable
时除外),即使它们未初始化,即使您使用的是 LEFT OUTER JOIN 到 [=12] =].因此 A_ID
将始终有一个值,并且 BoolProp
将始终为真。
另一方面,Reference Types
将默认为 NULL
。
根据DefaultIfEmpty method definition,以下代码片段是等价的:
List<C> list = new List<C>() { }; // empty list
List<C> listDefault = list.DefaultIfEmpty().ToList();
和
List<C> listDefault = new List<C>() { null }; // Since default(C) is null
因此,当您使用 g.DefaultIfEmpty() 时,您将获得一个唯一的 cc 对象,该对象为空,因此行:
BoolProp = cc.A_Id != null
将抛出 NullReferenceException,因为 cc 为空。
最后条件好像应该是:
BoolProp = cc != null
除此之外,还有一个小例子,它通过单元测试展示了不同之处:
[TestMethod]
public void TestMethod_DefaultifEmpty()
{
ListA = new List<A>()
{
new A { Id=1, Value="111" },
new A { Id=2, Value="222" },
};
ListC = new List<C>()
{
new C { Id=1, A_Id=1 }
};
Assert.AreEqual(2, MyMethod().Count());
}
public List<A> ListA { get; set; }
public List<C> ListC { get; set; }
public IEnumerable<A_DTO> MyMethod() =>
from a in ListA
join c in ListC
on a.Id equals c.A_Id into g
from cc in g.DefaultIfEmpty()
select new A_DTO
{
Id = a.Id,
Value = a.Value,
//BoolProp = cc.A_Id != null
BoolProp = cc != null // replace by previous line to see the difference
};
我有 EF 模型 class,我决定用一个 bool
属性:
class A
{
public int Id { get; set; }
public string Value { get; set; }
}
class A_DTO : A
{
public bool BoolProp { get; set; }
}
class C
{
public int Id { get; set; }
public int A_Id { get; set; }
}
然后我写了一个方法,它将 return A
集合与其他 C
集合连接起来,其中包含 A <=> C 映射(好吧,在现实世界中例如它包含一些 SystemId
和 linq 查询将由 2 列连接)和 returns A_DTO
集合:
internal IQueryable<A_DTO> MyMethod() =>
from a in dbContext.A
join c in dbContext.A_C_Mapping
on a.Id equals c.A_Id into g
from cc in gg.DefaultIfEmpty()
select new A_DTO
{
Id = a.Id,
Value = a.Value,
BoolProp = cc.A_Id != null //<- will be always true, well, that what says warning message
}
(dbContext
是我的 EF 上下文对象)
当然因为 cc.A_Id
不是可为空的 int
会出现警告消息,说
"The result of expression will be always
'true'
since the value of typeint
is never equal tonull
value of typeint?
"
这是真的,但实际上我得到的结果完全正确,因为我的左外连接 return null
s 在 C
集合中缺少映射时。
所以问题是:这样做并保持原样是正确的方法,还是我需要以其他方式实施?
如果 cc.A_Id 永远不会变为 null 并且您想设置 BoolProp 的值,那么您可以通过将 select 更改为以下内容来删除警告:
select new A_DTO
{
Id = a.Id,
Value = a.Value,
BoolProp = true
}
Value Types
将始终具有默认值(将它们声明为 Nullable
时除外),即使它们未初始化,即使您使用的是 LEFT OUTER JOIN 到 [=12] =].因此 A_ID
将始终有一个值,并且 BoolProp
将始终为真。
另一方面,Reference Types
将默认为 NULL
。
根据DefaultIfEmpty method definition,以下代码片段是等价的:
List<C> list = new List<C>() { }; // empty list
List<C> listDefault = list.DefaultIfEmpty().ToList();
和
List<C> listDefault = new List<C>() { null }; // Since default(C) is null
因此,当您使用 g.DefaultIfEmpty() 时,您将获得一个唯一的 cc 对象,该对象为空,因此行:
BoolProp = cc.A_Id != null
将抛出 NullReferenceException,因为 cc 为空。
最后条件好像应该是:
BoolProp = cc != null
除此之外,还有一个小例子,它通过单元测试展示了不同之处:
[TestMethod]
public void TestMethod_DefaultifEmpty()
{
ListA = new List<A>()
{
new A { Id=1, Value="111" },
new A { Id=2, Value="222" },
};
ListC = new List<C>()
{
new C { Id=1, A_Id=1 }
};
Assert.AreEqual(2, MyMethod().Count());
}
public List<A> ListA { get; set; }
public List<C> ListC { get; set; }
public IEnumerable<A_DTO> MyMethod() =>
from a in ListA
join c in ListC
on a.Id equals c.A_Id into g
from cc in g.DefaultIfEmpty()
select new A_DTO
{
Id = a.Id,
Value = a.Value,
//BoolProp = cc.A_Id != null
BoolProp = cc != null // replace by previous line to see the difference
};