С# Возможно ли предоставить лямбда, когда требуется интерфейс?

В некотором классе метод 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 вы можете использовать Прогресс класс