С# Возможно ли предоставить лямбда, когда требуется интерфейс?
В некотором классе метод A, мне нужно вызвать библиотечный метод B, который принимает как IProgress<Object>
как параметр.
Обычно я могу либо реализовать IProgress<Object>
как часть класса, где находится A, а затем передать "this" методу B. Или, возможно, я могу создать новый класс, единственная цель которого - реализовать IProgress<Object>
и обработать его правильно - тогда в этом случае я создам экземпляр этого класса и передам его в B.
Но я действительно хочу, чтобы моя реализация IProgress<Object>
отображалась прямо внутри метода, в котором я вызываю B, так что меньше визуального отключения между вызывающим кодом и реализацией IProgress<Object>
, (Я считаю, что моя реализация IProgress является своего рода частной, не разделяемой деталью вызывающего метода, и поэтому я не хочу, чтобы моя реализация IProgress<Object>
была в целом отдельном методе, возможно, всего другого класса).
То, что я пытался сделать, это использовать лямбда, в котором я буду определять свою короткую обработку прогресса, а затем как-то передать эту лямбду в B, например:
method in class A {
...
Action<Object> Report = (m) => { // do something useful with m };
B(Report)
}
method B(IProgress<Object> reporter) {
reporter.Report(some object)
}
Конечно, я знаю, почему это не сработает, как есть - B хочет объект, реализующий IProgress<Object>
, и я передам ему объект Action.
Есть ли способ достичь того, чего я пытаюсь достичь? (IE имеет мою реализацию, если IProgress<Object>
появляется внутри метода A?
Ответы
Ответ 1
Делегаты не могут реализовывать интерфейсы (напрямую).
Приходят на ум два хороших варианта:
-
Измените определение метода, который вы вызываете, для выбора типов делегатов вместо типа IProgress
. (Если возможно, это будет предпочтительный вариант)
-
Создайте новый тип, который реализует необходимый вам интерфейс, и принимает делегат в качестве параметра для реализации этой функциональности.
И пример # 2, зависящий от интерфейса, может выглядеть примерно так:
interface IProgress<T>
{
void doStuff(T param);
}
class LambdaProgress<T> : IProgress<T>
{
Action<T> action;
public LambdaProgress(Action<T> action)
{
this.action = action;
}
public void doStuff(T param)
{
action(param);
}
}
тогда вы сможете сделать что-то вроде:
B(new LambdaProgress<Object>(obj => ...));
Ответ 2
Нет, вы не можете предоставить lambda, когда требуется интерфейс.
Но вы можете предоставить анонимный объект, используя impromptu-interface.
Этот пример представлен на странице проекта:
//Anonymous Class
var anon = new
{
Prop1 = "Test",
Prop2 = 42L,
Prop3 = Guid.NewGuid(),
Meth1 = Return<bool>.Arguments<int>(it => it > 5)
}
var myInterface = anon.ActLike<IMyInterface>();
В вашем примере это может быть:
A
{
// Create an anonymous object.
var anonymous = new
{
// Define a method that needs an "object" parameter and returns nothing.
Report = Return.Arguments<object>(m =>
{
// Do whatever you want to do when Report is called.
})
}
// Get your anonymous object as an IProgress<Object>.
var obj = anonymous.ActLike<IProgress<Object>>
// Call B.
B(obj);
}
Ответ 3
Предполагая, что ваш интерфейс IProgress<object>
имеет только один метод void Report(object)
и что вы управляете API, вы можете просто переопределить методы, которым в настоящее время требуется параметр типа IProgress<object>
, вместо этого требуется новый тип делегата:
public delegate void ProgressReportDelegate(object someObject);
Тогда ваш пример может измениться на:
method in class A {
...
Action<Object> Report = (m) => { // do something useful with m };
B(Report)
}
method B(ProgressReportDelegate reporter) {
reporter(someObject);
}
Для более сложных интерфейсов или того, где вы не управляете API (и поэтому не можете изменить метод для принятия делегата, а не для объекта, реализующего этот интерфейс), это не совсем вариант, но это будет работают в вашем случае.
Ответ 4
С .NET 4.5 вы можете использовать Прогресс класс