2010-09-07 1 views
2

У меня есть контракт интерфейс, который выглядит следующим образом:Выражение <Func<T,bool>> - Как обрабатывать неоднозначные подписи?

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 и Bar являются конкретные классы, наследуемые от абстрактного класса FooBar.

Теперь им нарваться проблемы при попытке вызвать эти методы:

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

Это становится «Ambigious Призыва» ошибки, которые отчасти имеет смысл, потому что свойство «UserId» существует на абстрактном «FooBar "(и, следовательно, существует и на Foo и Bar).

Итак, как я могу это преодолеть?

Мне нравится внешний вид моего интерфейса (перегруженные методы предикатов), как с точки зрения intellisense из вызывающего кода, существует только одно имя метода.

Почему у меня есть мой интерфейс? Ну, некоторые сценарии я хочу вернуть только «Foo» или «Bar», иногда мне нужен смешанный пакет, поэтому мне нужно вернуть абстрактный тип - смысл?

В любом случае, на очевидный вопрос - есть ли способ обойти это? (кроме переименования моих методов интерфейса)? (и, таким образом, скомпрометируя простоту)

+0

просто была мысль - Есть ли способ создать классы, которые происходят из Expression > и использовать их в моей сигнатуре интерфейса? (и, таким образом, решение проблемы двусмысленности)? – RPM1984

+0

Выражение/Func - это запечатанные классы, dang. Im в убытке здесь! – RPM1984

ответ

1

Вы можете объявить метод быть общим, а затем вам нужно только один метод:

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

, а затем вы можете назвать это так:

var foo = myService.FindByPredicate<Foo>(f => f.UserId == 1); 
+0

Спасибо, но мне нужно применять определенные операции в зависимости от типа - поэтому я не могу пузырить его до общего T (ну, я могу, но мне нужно, если T - Foo и т. Д.). Мне нужно поддерживать интерфейс так, как есть. Спасибо, тo. – RPM1984

+0

@ RPM1984: достаточно справедливо - но я бы сказал, что это плохой дизайн интерфейса. То, что вы хотите применить определенные операции, является детальностью реализации и должно быть закрыто для класса, который реализует интерфейс. Да, вам понадобится * one * 'if', чтобы выбрать правильный частный метод для вызова. Я бы счел это правильным решением. – Timwi

+0

@Timwi - как будет объявлен интерфейс? У меня есть: 'public interface IFooBarService'. и он дает ошибку «не может разрешить символ T» – RPM1984

1

Вы можете объявить тип f в лямбда, а не предполагать его вывод. Таким образом, применима только одна перегрузка.

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); 
+0

Nice (+1). Благодарю. Предпочитаю не бросать, но я бы выбрал это, включая простоту интерфейса. – RPM1984

+0

RPM1984: Это не литье; он просто объявляет тип 'f' вместо того, чтобы дать компилятору его вывод. – Gabe

+0

@Gabe - ты прав. Спасибо за разъяснения. – RPM1984