|| (或)C# 中的 Linq 运算符
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/772261/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
The || (or) Operator in Linq with C#
提问by Ev.
I'm using linq to filter a selection of MessageItems. The method I've written accepts a bunch of parameters that might be null. If they are null, the criteria for the file should be ignored. If it is not null, use it to filter the results.
我正在使用 linq 来过滤选择的 MessageItems。我编写的方法接受一堆可能为空的参数。如果它们为空,则应忽略文件的标准。如果它不为空,则使用它来过滤结果。
It's my understanding that when doing an || operation is C#, if the first expression is true, the second expression should not be evaluated.
我的理解是在做 || 时 操作是 C#,如果第一个表达式为真,则不应计算第二个表达式。
e.g.
例如
if(ExpressionOne() || ExpressionTwo())
{
// only ExpressionOne was evaluated because it was true
}
now, in linq, I'm trying this:
现在,在 linq 中,我正在尝试:
var messages = (from msg in dc.MessageItems
where String.IsNullOrEmpty(fromname) || (!String.IsNullOrEmpty(fromname) && msg.FromName.ToLower().Contains(fromname.ToLower()))
select msg);
I would have thought this would be sound, because String.IsNullOrEmpty(fromname)
would equal true and the second part of the || wouldn't get run.
我会认为这会是合理的,因为String.IsNullOrEmpty(fromname)
等于 true 和 || 的第二部分 不会跑。
However it does get run, and the second part
但是它确实运行了,第二部分
msg.FromName.ToLower().Contains(fromname.ToLower()))
throws a null reference exception (because fromname
is null)!! - I get a classic "Object reference not set to an instance of an object" exception.
抛出空引用异常(因为fromname
为空)!!- 我收到一个经典的“对象引用未设置为对象的实例”异常。
Any help?
有什么帮助吗?
采纳答案by ShuggyCoUk
Have a read of this documentationwhich explains how linq and c# can experience a disconnect.
阅读此文档,其中解释了 linq 和 c# 如何断开连接。
Since Linq expressions are expected to be reduced to something other than plain methods you may find that this code breaks if later it is used in some non Linq to Objects context.
由于预期 Linq 表达式将被简化为普通方法以外的其他内容,因此您可能会发现,如果稍后在某些非 Linq to Objects 上下文中使用此代码,则该代码会中断。
That said
那说
String.IsNullOrEmpty(fromname) ||
( !String.IsNullOrEmpty(fromname) &&
msg.FromName.ToLower().Contains(fromname.ToLower())
)
Is badly formed since it should really be
形状不正确,因为它真的应该是
String.IsNullOrEmpty(fromname) ||
msg.FromName.ToLower().Contains(fromname.ToLower())
which makes it nice and clear that you are relying on msg and msg.FromName to both be non null as well.
这使得您很清楚您依赖 msg 和 msg.FromName 都为非空值。
To make your life easier in c# you could add the following string extension method
为了让您在 c# 中的生活更轻松,您可以添加以下字符串扩展方法
public static class ExtensionMethods
{
public static bool Contains(
this string self, string value, StringComparison comparison)
{
return self.IndexOf(value, comparison) >= 0;
}
public static bool ContainsOrNull(
this string self, string value, StringComparison comparison)
{
if (value == null)
return false;
return self.IndexOf(value, comparison) >= 0;
}
}
Then use:
然后使用:
var messages = (from msg in dc.MessageItems
where msg.FromName.ContainsOrNull(
fromname, StringComparison.InvariantCultureIgnoreCase)
select msg);
However this is not the problem. The problem is that the Linq to SQL aspects of the system are trying to use the fromname
value to construct the querywhich is sent to the server.
然而,这不是问题。问题在于系统的 Linq to SQL 方面正在尝试使用该fromname
值来构造发送到服务器的查询。
Since fromname
is a variable the translation mechanism goes off and does what is asked of it (producing a lower case representation of fromname
even if it is null, which triggers the exception).
由于fromname
是一个变量,翻译机制会关闭并执行要求的操作(fromname
即使它为空,也会产生小写表示,这会触发异常)。
in this case you can either do what you have already discovered: keep the query as is but make sure you can always create a non null fromname value with the desired behaviour even if it is null.
在这种情况下,您可以执行您已经发现的操作:保持查询原样,但确保您始终可以创建具有所需行为的非空 fromname 值,即使它为空。
Perhaps better would be:
也许更好的是:
IEnumerable<MessageItem> results;
if (string.IsNullOrEmpty(fromname))
{
results = from msg in dc.MessageItems
select msg;
}
else
{
results = from msg in dc.MessageItems
where msg.FromName.ToLower().Contains(fromname)
select msg;
}
This is not so great it the query contained other constraints and thus invovled more duplication but for the simple query actually should result in more readable/maintainable code. This is a pain if you are relying on anonymous types though but hopefully this is not an issue for you.
这并不是很好,因为查询包含其他约束,因此会导致更多的重复,但对于简单的查询,实际上应该导致更易读/可维护的代码。如果您依赖匿名类型,这会很痛苦,但希望这对您来说不是问题。
回答by Brian
Are you sure it's 'fromname' that's null and not 'msg.FromName' that's null?
您确定是“fromname”为空而不是“msg.FromName”为空吗?
回答by Nordes
Like Brian said, I would look if the msg.FromName is null before doing the ToLower().Contains(fromname.ToLower()))
就像布赖恩说的那样,我会在执行 ToLower().Contains(fromname.ToLower())) 之前查看 msg.FromName 是否为空
回答by Ev.
Okay. I found Asolution.
好的。我找到了一个解决方案。
I changed the offending line to:
我将违规行更改为:
where (String.IsNullOrEmpty(fromemail) || (msg.FromEmail.ToLower().Contains((fromemail ?? String.Empty).ToLower())))
It works, but it feels like a hack. I'm sure if the first expression is true the second should not get evaluated.
它有效,但感觉就像一个黑客。我确定第一个表达式是否为真,第二个不应该被评估。
Would be great if anyone could confirm or deny this for me...
如果有人可以为我确认或否认这一点,那就太好了......
Or if anyone has a better solution, please let me know!!!
或者如果有人有更好的解决方案,请告诉我!!!
回答by Lazarus
You are correct that the second conditional shouldn't get evaluated as you are using the short-circuit comparitors (see What is the best practice concerning C# short-circuit evaluation?), however I'd suspect that the Linq might try to optimise your query before executing it and in doing so might change the execution order.
您在使用短路比较器时不应评估第二个条件是正确的(请参阅关于 C# 短路评估的最佳实践是什么?),但是我怀疑 Linq 可能会尝试优化您的在执行之前查询,这样做可能会改变执行顺序。
Wrapping the whole thing in brackets also, for me, makes for a clearer statement as the whole 'where' condition is contained within the parenthases.
对我来说,将整个内容括在括号中也可以使声明更清晰,因为整个“where”条件都包含在括号内。
回答by Lucas
If you are using LINQ to SQL, you cannotexpect the same C# short-circuit behavior in SQL Server. See this questionabout short-circuit WHERE
clauses (or lack thereof) in SQL Server.
如果您使用的是 LINQ to SQL,则不能期望 SQL Server 中出现相同的 C# 短路行为。看到这个问题约短路WHERE
条款(或其缺乏)在SQL Server。
Also, as I mentioned in a comment, I don't believe you are getting this exception in LINQ to SQL because:
另外,正如我在评论中提到的,我不相信您会在 LINQ to SQL 中遇到此异常,因为:
- Method
String.IsNullOrEmpty(String)
has no supported translation to SQL, so you can't use it in LINQ to SQL. - You wouldn't be getting the NullReferenceException. This is a managed exception, it would only happen client-side, not in SQL Server.
- 方法
String.IsNullOrEmpty(String)
不支持转换为 SQL,因此您不能在 LINQ to SQL 中使用它。 - 你不会得到 NullReferenceException。这是一个托管异常,它只会发生在客户端,而不是在 SQL Server 中。
Are you sure this is not going through LINQ to Objects somewhere? Are you calling ToList() or ToArray() on your source or referencing it as a IEnumerable<T> before running this query?
你确定这不是在某个地方通过 LINQ to Objects 吗?在运行此查询之前,您是在源上调用 ToList() 或 ToArray() 还是将其作为 IEnumerable<T> 引用?
Update:After reading your comments I tested this again and realized some things. I was wrong about you not using LINQ to SQL. You were not getting the "String.IsNullOrEmpty(String) has no supported translation to SQL"
exception because IsNullOrEmpty()
is being called on a local variable, not an SQL column, so it isrunning client-side, even though you are using LINQ to SQL (not LINQ to Objects). Since it is running client-side, you canget a NullReferenceException
on that method call, because it is not translated to SQL, where you cannot get a NullReferenceException
.
更新:阅读您的评论后,我再次对此进行了测试并意识到了一些事情。我错了你没有使用 LINQ to SQL。你没有得到的"String.IsNullOrEmpty(String) has no supported translation to SQL"
异常,因为IsNullOrEmpty()
被调用的局部变量,而不是一个SQL列,所以它是运行客户端,即使您正在使用LINQ到SQL(LINQ不来对象)。由于它在客户端运行,因此您可以NullReferenceException
在该方法调用上获得,因为它没有转换为 SQL,在那里您无法获得NullReferenceException
.
One way to make your solution seem less hacky is be resolving fromname
's "null-ness" outside the query:
使您的解决方案看起来不那么笨拙的一种方法是fromname
在查询之外解析的“空性”:
string lowerfromname = String.IsNullOrEmpty(fromname) ? fromname : fromname.ToLower();
var messages = from msg in dc.MessageItems
where String.IsNullOrEmpty(lowerfromname) || msg.Name.ToLower().Contains(lowerfromname)
select msg.Name;
Note that this will not always be translated to something like (using your comments as example):
请注意,这不会总是被翻译成类似的东西(以您的评论为例):
SELECT ... FROM ... WHERE @theValue IS NULL OR @theValue = theValue
Its translation will be decided at runtime depending on whether fromname
is null or not. If it is null, it will translate without a WHERE
clause. If it is not null, it will translate with a simple "WHERE @theValue = theValue
", without null check in T-SQL.
它的翻译将在运行时根据是否fromname
为空来决定。如果它为空,它将在没有WHERE
子句的情况下进行翻译。如果它不为空,它将用一个简单的“ WHERE @theValue = theValue
”来翻译,在 T-SQL 中没有空检查。
So in the end, the question of whether it will short-circuit in SQL or not is irrelevant in this case because the LINQ to SQL runtime will emit different T-SQL queries if fromname
is null or not. In a sense, it is short-circuited client-side before querying the database.
所以最后,它是否会在 SQL 中短路的问题在这种情况下是无关紧要的,因为 LINQ to SQL 运行时会发出不同的 T-SQL 查询,fromname
无论是否为空。从某种意义上说,它是在查询数据库之前短路客户端。