Assignment to expression using ternary operator [duplicate] - c#

This question already has answers here:
Can't use ternary operator to assign Linq expression
(3 answers)
Closed 7 years ago.
I'm trying to understand why the first one is OK,
Expression<Func<Foo, bool>> filterExpression = null;
if (id.HasValue) filterExpression = w => w.Id == id.Value;
and this one complains:
Expression<Func<Foo, bool>> filterExpression = id.HasValue ? w => w.Id == id.Value : null;
In the second one, I'm receiving error "Cannot resolve symbol 'Id'". I can't see why "w" is not resolved as my class "Foo" in this case, since the expression definition on the left-side contains such information.
Thanks.

Your statement has several problems.
First, the symbol w is of type MerketNews and not Foo.
Second, you cannot define an expression using the (pseudo-)syntax
[ExpressionType] expr = [boolValue] ? [lambda] : null;
You must use
[ExpressionType] expr = [boolValue] ? [lambda] : ([ExpressionType])null;
In your case:
Expression<Func<Foo, bool>> filterExpression = id.HasValue ? w => w.Id == id.Value : (Expression<Func<Foo, bool>>)null;

Related

Func<int, int> from conditional expression [duplicate]

Generally, when using the conditional operator, here's the syntax:
int x = 6;
int y = x == 6 ? 5 : 9;
Nothing fancy, pretty straight forward.
Now, let's try to use this when assigning a Lambda to a Func type. Let me explain:
Func<Order, bool> predicate = id == null
? p => p.EmployeeID == null
: p => p.EmployeeID == id;
That's the same syntax, and should work? Right? For some reason that doesn't. The compiler gives this nice cryptic message:
Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'lambda expression'
I then went ahead and changed the syntax and this way it did work:
Func<Order, bool> predicate = id == null
? predicate = p => p.EmployeeID == null
: predicate = p => p.EmployeeID == id;
I'm just curious as to why it doesn't work the first way?
(Side note: I ended up not needing this code, as I found out that when comparing an int value against null, you just use object.Equals)
You can convert a lambda expression to a particular target delegate type, but in order to determine the type of the conditional expression, the compiler needs to know the type of each of the second and third operands. While they're both just "lambda expression" there's no conversion from one to the other, so the compiler can't do anything useful.
I wouldn't suggest using an assignment, however - a cast is more obvious:
Func<Order, bool> predicate = id == null
? (Func<Order, bool>) (p => p.EmployeeID == null)
: p => p.EmployeeID == id;
Note that you only need to provide it for one operand, so the compiler can perform the conversion from the other lambda expression.
The C# compiler cannot infer the type of the created lambda expression because it processes the ternary first and then the assignment. you could also do:
Func<Order, bool> predicate =
id == null ?
new Func<Order,bool>(p => p.EmployeeID == null) :
new Func<Order,bool>(p => p.EmployeeID == id);
but that just sucks,
you could also try
Func<Order, bool> predicate =
id == null ?
(Order p) => p.EmployeeID == null :
(Order p) => p.EmployeeID == id;
Let me have my own example since I had the same problem, too (with the hope that the example be helpful for others):
My Find method is generic method that gets Expression<Func<T, bool>> as predicate and gives List<T> as output.
I wanted to find countries, but I need all of them if language list was empty, and filtered list, if language list was filled.
First I used the Code as below:
var countries=
Find(languages.Any()
? (country => languages.Contains(country.Language))
: (country => true));
But exactly I get the error :there is no implicit conversion between lambda expression and lambda expression.
The problem was that, we have just two lambda expressions here, and nothing else, for example, what is country => true exactly?? We have to determine the type of at least one of lambda expressions. If just of one of the expressions be determined, then the error will be omitted. But for make the code more readable, I extracted both lambda expressions, and used the variable instead, as below:
Expression<Func<Country, bool>> getAllPredicate = country => true;
Expression<Func<Country, bool>> getCountriesByLanguagePredicate = country => languages.Contains(country.Language);
var countries= Find(languages.Any()
? getCountriesByLanguagePredicate
: getAllPredicate);
I emphasize that, if I just determined one of the expression's type, the error will be fixed.
Just an update - in C# 10, it IS now possible for the compiler to infer the 'natural type' of a lambda, provided that the input type(s) are provided, e.g.
var evenFilter = (int i) => i % 2 == 0; // evenFilter inferred as `Func<int, bool>`
This also means that 0 input Funcs and Actions can be inferred:
var zeroInputFunc = () => 44 % 2 == 0;
var myAction = () => {Console.WriteLine("Foo");};
However, this won't work:
var filter = i => i % 2 == 0; << Error: The delegate type could not be inferred
As a result, it is now possible to do what the OP originally wanted to do, provided that at least the input types are provided, e.g.
Func<int, bool> myPredicate = selectorFlag
? i => i % 2 == 0
: i => i % 2 == 1;
However, this still isn't permitted:
var myPredicate = selectorFlag
? (int i) => i % 2 == 0
: (int i) => i % 2 == 1;
Error : no implicit conversion between 'lambda expression' and 'lambda expression'

how to add to linq query expression if a condition is met dynamically [duplicate]

This question already has answers here:
Linq: adding conditions to the where clause conditionally
(9 answers)
Closed 3 years ago.
I have an expression tree built like this
Expression<Func<User, bool>> match = o =>
o.Name == viewModel.Name
&& orders.Contains(o.User.Company.CompanyId.ToString())
&& o.dept == viewModel.dept
I only want to include the line
o.dept == viewModel.dept
when viewModel.dept is 1, 2 or 3 and not include this condition in the expression tree, if it’s any other value.
This code is in a function that gets a viewModel as parameter and the values in the viewModel are used to query the EF model User.
Currently I have 2 separate expression trees to meet this scenario. Is there any better solution?
Thank you
I finally got a graceful solution using LINQKit (https://github.com/scottksmith95/LINQKit) and its PredicateBuilder
This is what I did.
var match = PredicateBuilder.New<User>(true);
match = match.And(o => o.Name == viewModel.Name);
match = match.And(o =>orders.Contains(o.User.Company.CompanyId.ToString()));
if(viewModel.dept==1 || viewModel.dept == 2 || viewModel.dept==3)
{
match = match.And(o=>o.dept == viewModel.dept);
}
return match;

Syntax for this conditional expression [duplicate]

This question already has answers here:
Why doesn't the C# ternary operator work with delegates?
(2 answers)
Closed 4 years ago.
I have the following code in an if else statement (c#).
if (!string.IsNullOrEmpty(ParentKey))
{
Build(x => x.ParentKey == ParentKey);
}
else
{
Build(x => x.Url == Request.Url.GetLeftPart(UriPartial.Path));
}
However, I would have preferred to use a condition expression like this:
var r = !string.IsNullOrEmpty(ParentKey)
? 100
: 1000;
Normally, this wouldn't be an issue but the var is a Func<SiloNode, bool> meaning the expression would look like this:
Func<SiloNode, bool> predicate = !string.IsNullOrEmpty(ParentKey)
? x => x.ParentKey == ParentKey
: x => x.Url == Request.Url.GetLeftPart(UriPartial.Path);
Unsurprisingly, the code above gives me a syntax error but I'm not sure whether its because I'm using the wrong syntax or it is just not possible.
Anyone shed any light?
A lambda is just a lambda, and your two lambdas don't necessarily correspond to a Func object (even though the signatures match).
You can actually cast one (or both) of those lambdas into the appropriate Func type, and then the ?: operator will work.
Func<SiloNode, bool> predicate = !string.IsNullOrEmpty(ParentKey)
? (Func<SiloNode, bool>)(x => x.ParentKey == ParentKey)
: x => x.Url == Request.Url.GetLeftPart(UriPartial.Path);

How to Combine two lambdas [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
combining two lamba expressions in c#
I have two following expressions:
Expression<Func<string, bool>> expr1 = s => s.Length == 5;
Expression<Func<string, bool>> expr2 = s => s == "someString";
Now I need to combine them with OR. Something like this:
Expression.Or(expr1, expr2)
Is there any way to make this similar to above code way like:
expr1 || expr2
I understand in this example I can just combine it in the first place:
Expression<Func<string, bool>> expr = s => s.Length == 5 || s == "someString"
but I can't do it in my real code as I get expr1 and expr2 as arguments to the method.
To complete Eric's answer, using the new ExpressionVisitor introduced in .NET 4 rather than a custom rewriter:
internal class ParameterReplacer : ExpressionVisitor {
private readonly ParameterExpression _parameter;
protected override Expression VisitParameter(ParameterExpression node) {
return base.VisitParameter(_parameter);
}
internal ParameterReplacer(ParameterExpression parameter) {
_parameter = parameter;
}
}
class Program {
static void Main(string[] args) {
Expression<Func<string, bool>> expr1 = s => s.Length == 5;
Expression<Func<string, bool>> expr2 = s => s == "someString";
var paramExpr = Expression.Parameter(typeof(string));
var exprBody = Expression.Or(expr1.Body, expr2.Body);
exprBody = (BinaryExpression) new ParameterReplacer(paramExpr).Visit(exprBody);
var finalExpr = Expression.Lambda<Func<string, bool>>(exprBody, paramExpr);
}
}
The problem is that the "s" parameters in each lambda have the same name and same type, but they are different parameters. Parameters have reference identity, not value identity. Simply combining the two bodies of the existing expression trees into a third expression tree effectively makes:
s => s1.Length == 5 || s2 == "somestring"
which doesn't make any sense. What you want to do is write a visitor that does a search-and-replace of the parameter s with a new parameter that you will then use as the parameter to the new lambda expression.
See this related question for more details:
Combining two lambda expressions in c#

How can I assign a Func<> conditionally between lambdas using the conditional ternary operator?

Generally, when using the conditional operator, here's the syntax:
int x = 6;
int y = x == 6 ? 5 : 9;
Nothing fancy, pretty straight forward.
Now, let's try to use this when assigning a Lambda to a Func type. Let me explain:
Func<Order, bool> predicate = id == null
? p => p.EmployeeID == null
: p => p.EmployeeID == id;
That's the same syntax, and should work? Right? For some reason that doesn't. The compiler gives this nice cryptic message:
Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'lambda expression'
I then went ahead and changed the syntax and this way it did work:
Func<Order, bool> predicate = id == null
? predicate = p => p.EmployeeID == null
: predicate = p => p.EmployeeID == id;
I'm just curious as to why it doesn't work the first way?
(Side note: I ended up not needing this code, as I found out that when comparing an int value against null, you just use object.Equals)
You can convert a lambda expression to a particular target delegate type, but in order to determine the type of the conditional expression, the compiler needs to know the type of each of the second and third operands. While they're both just "lambda expression" there's no conversion from one to the other, so the compiler can't do anything useful.
I wouldn't suggest using an assignment, however - a cast is more obvious:
Func<Order, bool> predicate = id == null
? (Func<Order, bool>) (p => p.EmployeeID == null)
: p => p.EmployeeID == id;
Note that you only need to provide it for one operand, so the compiler can perform the conversion from the other lambda expression.
The C# compiler cannot infer the type of the created lambda expression because it processes the ternary first and then the assignment. you could also do:
Func<Order, bool> predicate =
id == null ?
new Func<Order,bool>(p => p.EmployeeID == null) :
new Func<Order,bool>(p => p.EmployeeID == id);
but that just sucks,
you could also try
Func<Order, bool> predicate =
id == null ?
(Order p) => p.EmployeeID == null :
(Order p) => p.EmployeeID == id;
Let me have my own example since I had the same problem, too (with the hope that the example be helpful for others):
My Find method is generic method that gets Expression<Func<T, bool>> as predicate and gives List<T> as output.
I wanted to find countries, but I need all of them if language list was empty, and filtered list, if language list was filled.
First I used the Code as below:
var countries=
Find(languages.Any()
? (country => languages.Contains(country.Language))
: (country => true));
But exactly I get the error :there is no implicit conversion between lambda expression and lambda expression.
The problem was that, we have just two lambda expressions here, and nothing else, for example, what is country => true exactly?? We have to determine the type of at least one of lambda expressions. If just of one of the expressions be determined, then the error will be omitted. But for make the code more readable, I extracted both lambda expressions, and used the variable instead, as below:
Expression<Func<Country, bool>> getAllPredicate = country => true;
Expression<Func<Country, bool>> getCountriesByLanguagePredicate = country => languages.Contains(country.Language);
var countries= Find(languages.Any()
? getCountriesByLanguagePredicate
: getAllPredicate);
I emphasize that, if I just determined one of the expression's type, the error will be fixed.
Just an update - in C# 10, it IS now possible for the compiler to infer the 'natural type' of a lambda, provided that the input type(s) are provided, e.g.
var evenFilter = (int i) => i % 2 == 0; // evenFilter inferred as `Func<int, bool>`
This also means that 0 input Funcs and Actions can be inferred:
var zeroInputFunc = () => 44 % 2 == 0;
var myAction = () => {Console.WriteLine("Foo");};
However, this won't work:
var filter = i => i % 2 == 0; << Error: The delegate type could not be inferred
As a result, it is now possible to do what the OP originally wanted to do, provided that at least the input types are provided, e.g.
Func<int, bool> myPredicate = selectorFlag
? i => i % 2 == 0
: i => i % 2 == 1;
However, this still isn't permitted:
var myPredicate = selectorFlag
? (int i) => i % 2 == 0
: (int i) => i % 2 == 1;
Error : no implicit conversion between 'lambda expression' and 'lambda expression'

Categories

Resources