views:

74

answers:

2

Hi Guys,

I have an interface contract that looks like this:

ICollection<FooBar> FindByPredicate(Expression<Func<FooBar,bool>> predicate);
ICollection<Foo> FindByPredicate(Expression<Func<Foo,bool>> predicate);
ICollection<Bar> FindByPredicate(Expression<Func<Bar,bool>> predicate);

Foo and Bar are concrete classes which inherit from the FooBar abstract class.

Now, im running into problems when trying to invoke these methods:

var foo = myService.FindByPredicate(f => f.UserId == 1);

It's getting "Ambigious invocation" errors, which kind of makes sense, because the property "UserId" exists on the abstract "FooBar" type (and thus exists on Foo and Bar as well).

So, how can i overcome this?

I like the look of my interface (overloaded predicate methods) as from an intellisense point of view from the calling code, there is only one method name.

Why do i have my interface like that? Well, some scenarios i wish to return only "Foo" or "Bar", other times i need a mixed bag, hence i need to return the abstract type - make sense?

Anyway, onto the obvious question - is there a way around this? (other than renaming my interface methods)? (and thus compromising the simplicity)

+1  A: 

You can declare the type of f in the lambda rather than let it be inferred. That way, only one overload will be applicable.

var foo = myService.FindByPredicate((Foo f) => f.UserId == 1);
var bar = myService.FindByPredicate((Bar f) => f.UserId == 1);
var foobar = myService.FindByPredicate((FooBar f) => f.UserId == 1);
Quartermeister
Nice (+1). Thanks. Would prefer not to cast, but i would choose that over comprising the simplicity of the interface.
RPM1984
RPM1984: This isn't casting; it's just declaring the type of `f` instead of letting the compiler infer it.
Gabe
@Gabe - youre right. Thanks for the clarification.
RPM1984
@Quartermeister - sorry to take the answer away from you =) But i prefer Timwi's answer because it reduces the amount of definitions in my interface contract. Thanks for your help though.
RPM1984
+1  A: 

You can declare the method to be generic, and then you need only one method:

public interface IFooBarService
{
    ICollection<T> FindByPredicate<T>(Expression<Func<T, bool>> predicate)
        where T : FooBar;
}

and then you can call it like this:

var foo = myService.FindByPredicate<Foo>(f => f.UserId == 1);
Timwi
Thanks, but i need to apply specific operations depending on the type - therefore i cannot bubble it up to the generic T (well i can, but i'll need if T is Foo, etc). I need to keep my interface the way it is. Thanks tho.
RPM1984
@RPM1984: Fair enough — but I would argue that’s bad interface design. That you want to apply specific operations is an implementation detail and should be private to the class that implements the interface. Yes, you would need *one* `if` to select the right private method to call. I would consider that to be the correct solution.
Timwi
@Timwi - how would the interface be declared? I have: `public interface IFooBarService`. and it's giving error "cannot resolve symbol T"
RPM1984
@RPM1984: It doesn’t give that error for me. I’ve updated the answer to contain the entire interface declaration. All I changed here was to add `public interface IFooBarService` and `{`...`}` around it.
Timwi
@Timwi - thanks, i was missing the <T> after the FindByPredicate (duh!). I'll implement the code and see if my unit tests pass. This is no doubt the better solution as i had around 10 interface methods, this will bring it down to 1. Of course, it needs to work. =) I'll let you know...
RPM1984
@RPM1984: I’ve [pasted something here](http://pastebin.ca/1934785) that compiles.
Timwi
It works, all my unit tests worked. Thanks a ton!!!
RPM1984
+1 for taking the effort to try it out. (you didnt need to though)
RPM1984
By the way, i dont need the if at all (i thought i might). This is all i have in the method: `return _repository .Find() .OfType<T>() .Where(predicate) .Take(maxRows) .ToList();`
RPM1984
Perfect! Thanks for your vote ☺
Timwi