How to remove ambiguous method call in c# - c#

Here I have a simple scenario with two methods where I get an ambiguous invocation from one another:
This is a my code:
public IEnumerable<JobsViewModel> GetJobsViewModels(Guid vesselId, int status, Func<JobsNoSubsYpdcResult, bool> predicate = null)
=> predicate == null
? Mapper.Map<IEnumerable<JobsViewModel>>(_procedureService.Tech_GetJobsNoSubsYPDC(vesselId, status))
: Mapper.Map<IEnumerable<JobsViewModel>>(_procedureService.Tech_GetJobsNoSubsYPDC(vesselId, status).Where(predicate));
public IEnumerable<JobsViewModel> GetJobsViewModels(Guid vesselId, int status, Func<JobsViewModel, bool> predicate = null)
=> predicate == null
? GetJobsViewModels(vesselId, status)
: GetJobsViewModels(vesselId, status).Where(predicate);
I dont like changing the names of methods but from the second one I get the error message:
Ambiguous Invocation
I would like to call first one from second one like I am trying to do, does anybody know how can I do this without changing the method names otherwise I am supposed to change them ?

The best way to handle this is to not have two overloads that are ambiguous, that way you don't need to remember how to special-case this everywhere you call them.
Since your second overload calls the first I would say the most logical cause of action would be to remove the default parameter value for that delegate:
public IEnumerable<JobsViewModel> GetJobsViewModels(Guid vesselId, int status, Func<JobsViewModel, bool> predicate)
=> predicate == null
? GetJobsViewModels(vesselId, status)
: GetJobsViewModels(vesselId, status).Where(predicate);
You should also reason about the ability to call this method specifying the delegate parameter, but explicitly specifying null. If this is not really meant to be possible anyway (ie. the first part of the ternary expression is not really going to be used) then I would simplify the entire method:
public IEnumerable<JobsViewModel> GetJobsViewModels(Guid vesselId, int status, Func<JobsViewModel, bool> predicate)
=> GetJobsViewModels(vesselId, status).Where(predicate);
Now, having said all that I don't really see the point of this second method anyway, as on the outside I could simply tuck on a .Where(...) filter anyway. Since the method doesn't do anything clever with this predicate, like passing it on and doing it on a lower level, or optimizing the way it fetches data, then this is really just syntactic sugar.
However, that's just my opinion, and I admittedly don't know how all of this is going to be used. My real answer is the first part though, you really should avoid declaring ambiguous methods.
Also see this question: Breaking change in method overload resolution in C# 6 - explanation?.
Overload resolution changed when Roslyn entered the scene and they decided to document the change and make it breaking instead of reintroducing the old "bug".

You need to provide the optional argument and make its type clear to the compiler, even if the value is null:
GetJobsViewModels(vesselId, status, (Func<JobsNoSubsYpdcResult, bool>)null)
EDIT: In the particular case presented by the OP, Lasse's solution is preferable.
The solution shown in this answer here, on the other hand, can be applied generally, also when disambiguating overloads where the other overload does not have an optional parameter that can be made non-optional (e.g. when invoking some of the ArgumentNullException constructors).

The only way to do this would be to fill in the optional parameter with a value of the appropriate type, so that the compiler knows which overload to pick. For example:
public IEnumerable<JobsViewModel> GetJobsViewModels(
Guid vesselId,
int status,
Func<JobsViewModel, bool> predicate = null)
{
// We're never filtering by JobsNoSubsYpdcResult, but this
// satisfies overload resolution
Func<JobsNoSubsYpdcResult, bool> resultPredicate = null;
return predicate == null
? GetJobsViewModels(vesselId, status, resultPredicate)
: GetJobsViewModels(vesselId, status, resultPredicate).Where(predicate);
}
Or to avoid repetition:
public IEnumerable<JobsViewModel> GetJobsViewModels(
Guid vesselId,
int status,
Func<JobsViewModel, bool> predicate = null)
{
// We're never filtering by JobsNoSubsYpdcResult, but this
// satisfies overload resolution
Func<JobsNoSubsYpdcResult, bool> resultPredicate = null;
var allResults = GetJobsViewModels(vesselId, status, resultPredicate);
return predicate == null
? allResults : allResults.Where(predicate);
}
Or to avoid even more repetition, introduce your own extension method:
public static IEnumerable<T> OptionalWhere<T>(
this IEnumerable<T> source,
Func<T, bool> predicate) =>
predicate == null ? source : source.Where(predicate);
Then you can rewrite both methods to use that:
public IEnumerable<JobsViewModel> GetJobsViewModels(Guid vesselId, int status, Func<JobsNoSubsYpdcResult, bool> predicate = null) =>
Mapper.Map<IEnumerable<JobsViewModel>>(_procedureService.Tech_GetJobsNoSubsYPDC(vesselId, status))
.OptionalWhere(predicate);
public IEnumerable<JobsViewModel> GetJobsViewModels(
Guid vesselId,
int status,
Func<JobsViewModel, bool> predicate = null)
{
// We're never filtering by JobsNoSubsYpdcResult, but this
// satisfies overload resolution
Func<JobsNoSubsYpdcResult, bool> resultPredicate = null;
return GetJobsViewModels(vesselId, status, resultPredicate)
.OptionalWhere(predicate);
}
(You can use an expression-bodied method if you're happy to cast null to Func<JobsNoSubsYpdcResult, bool> or use a static field for that.)

If you really want what you say you want, you need to pass the proper predicate argument, e.g. GetJobsViewModels(vesselId, status, new Func<JobsNoSubsYpdcResult, bool>( x => true ));

Related

Expression Func with two input parameters for generic method

I want to integrate this expression
Expression<Func<Customer, string, bool>> paramCompareFunc = (cust, name) => cust.Company == name;
For this
private bool IsUnique<Entity>(DbSet<Entity> set,
string param,
Expression<Func<Entity, string, bool>> paramCompareFunc)
where Entity : class
{
var query = set.Where(paramCompareFunc); // how I can pass param to expression?
// var query = set.Where(paramCompareFunc(param)); // error here
...
How I can pass the second parameter to the expression?
I want to define different compare expressions for different entities (they don't have any same name field) and to have a possibility to pass this expression into my generic function.
The "easy" way is by changing your api to use a factory method to build the Expression you actually need;
Expression<Func<Customer, bool>> GetCompareFunc(string name) => (cust) => cust.Company == name;
While you could use ReplacingExpressionVisitor to swap the name parameter with a constant, that would have a negative impact on performance.

Find Predicate parameters

Is there a way to find parameters which is passed in predicate variable. Lets say, I have this method;
List<User> GetUsers(Predicate<UserModel> userPredicate)
{
// how to find what values are passed in userPredicate
}
Function Call:
GetUsers(_ => _.Name == "abc");
How can I find that in predicate, Name property has been set to "abc" within GetUsers function?
public static void GetUsers(Expression<Func<UserModel, bool>> predicate)
{
var expr = predicate.Body as BinaryExpression;
var value = expr.Right as ConstantExpression;
// this will be the value of that predicate Func
var yourvalue = value.Value;
}
This is one way to do it, and you have to be careful about casting those expressions because it might not work if you change the body of expression.
FYI, I wouldn't recommend this solution at all to achieve what you need to do, there are better ways to design it but it gives you are looking for.

Meaning of Func<object, bool>(p => true)

I saw this line of code:
var myFilter= val1 ? (Func<Person, bool>)(person=> true) : (person=> person.IsValid);
What does this part means?:
(Func<Person, bool>)(person=> true)
Then, the myFilter variable is being used to filter some data from a table...
return ctx.Persons.Where(myFilter).ToList();
What happens when val1 is true and the first part of the conditional (Func<Person, bool>)(person=> true) is selected?
The Where method takes a parameter of type Func<T, bool> - this is essentially a function that takes a T (in this case, Person) as a parameter, and returns a bool. The Where method evaluates this function for each object in the source list and filters to only those that result in true.
The delegate person => true defines a method that takes a Person as a parameter and always returns true, regardless of the Person object. When used in a Where clause, this will never filter out anything and returns the original set of items. (The cast to Func<T, bool> is required because of the use of var - the compiler can't determine the type of an anonymous delegate without a hint.)
So this code:
var myFilter = val1 ? (Func<Person, bool>)(person => true) : (person => person.IsValid);
return ctx.Persons.Where(myFilter).ToList();
does the same thing as this:
if (val1)
{
return ctx.Persons.ToList();
}
else
{
return ctx.Persons.Where(person => person.IsValid).ToList();
}
In such case the expression passed to Where returns true always - for any Person passed to it.
For the conditional operator to compile, the two expressions need to have the same type. Lambdas have unique types, so two lambdas don't have the same type. By casting (at least) one of these lambdas to a Func<Person, bool>, the common type of the two expression can be determined to be Func<Person, bool> by implicit conversion of the other lambda to the Func type.
So myFilter is now either the function that always returns true, or if val1 is false, actually does some filtering when used as a Where clause.

c# Predicate with no parameters

I'm trying to pass a Predicate to a function but it has no input. I'm actually trying to delay the computation until the call is made.
Is there a way to use c# Predicate type for that? If not why.
I know a way to do this with Func
Func<bool> predicate = () => someConditionBool;
But I want to do this:
Predicate<> predicate = () => someConditionBool;
If it's just a readability problem, then you can create your own delegate which returns boolean and don't have any parameters:
public delegate bool Predicate();
And use it this way
Predicate predicate = () => someConditionBool;
NOTE: You should keep in mind that everyone is familiar with default Action and Func delegates which are used in .NET, but Predicate is your custom delegate and it will be less clear at first time for average programmer.
Is there a way to use c# Predicate type for that? If not why?
Looking at the signature of Predicate<T> is shows that it requires an argument or parameter:
public delegate bool Predicate<in T>(T obj)
So the quick answer here would be no. Because you have no parameters in your anonymous function.
But if you play a little around you can give the predicate some irrelevant object which it will not use anyway, like an alibi parameter:
Func<bool> predicate = () => 1 == 1;
Predicate<object> predicate2 = (x) => 1 == 1;
and the call would look like this:
Console.WriteLine(predicate());
Console.WriteLine(predicate2(null));
And this compiles and returns the correct result

passing in nullable Expression<Func<T, TResult>>? as method parameter?

So i have a method
public IPagedList<MyObject> GetAll<T>(Expression<Func<MyObject, T>>? orderBy,
int pageNumber = 1, int pageSize = 10)
{
return dataContext.MyObjects
.OrderBy(orderBy.HasValue ? orderBy.Value : <WHAT GOES HERE?>)
.ToPagedList<MyObject>(pageNumber, pageSize);
}
My goal is to have the orderBy parameter optional, if orderBy is null then default the order to the property MyObject.Id.
I've tried .OrderBy(orderBy.Hasvalue ? orderBy.Value : x => x.Id) but getting this error:
Type of conditional expression cannot be determined because there is no implicit conversion between 'System.Func<MyObject, T>' and 'lambda expression'
What am I doing wrong?
Thanks!
There are a few problems with your code
Expression<TDelegate> is a class, so it's nullable already; you can simply test if orderBy == null. Nullable<T> has a generic constraint that T must be a struct, so Expression<Func<MyObject, T>>? won't compile.
Next you'll have the problem that because the type T isn't bound inside the method, but x.Id is. In other words, you won't be able to create use the conditional operator to choose between a value of Expression<Func<MyObject, T>> and Expression<Func<MyObject, int>> (assuming that Id is an int) while still maintaining type information to pass to the OrderBy method.
The solution is to use something along these lines:
public IPagedList<MyObject> GetAll<T>(Expression<Func<MyObject, T>> orderBy,
int pageNumber = 1, int pageSize = 10)
{
IQueryable<MyObject> objects = dataContext.MyObjects;
objects = (orderBy != null) ? objects.OrderBy(orderBy)
: objects.OrderBy(x => x.Id);
return objects.ToPagedList<MyObject>(pageNumber, pageSize);
}
The conditional operator works in this code because regardless of what you pass to OrderBy the return type will be the same, IQueryable<MyObject>.
Note also that you can't simply pass in a null value for orderBy, because T can't be inferred. You'd have to call it like this:
var results = MyClass.GetAll<int>(null);
Ultimately, you'd probably be better off creating two overloads, one that accepts an orderBy expression, and one that doesn't.

Categories

Resources