Преобразуйте этот делегат в анонимный метод или лямбда

Я плохо знаком со всеми анонимными функциями и мне нужна помощь. Я получил следующие результаты:

public void FakeSaveWithMessage(Transaction t)
{
    t.Message = "I drink goats blood";
}

public delegate void FakeSave(Transaction t);

public void SampleTestFunction()
{
    Expect.Call(delegate { _dao.Save(t); }).Do(new FakeSave(FakeSaveWithMessage));
}

Но это совершенно уродливо, и я хотел бы, чтобы внутри Do был анонимный метод или даже лямбда, если это возможно. Я пытался:

Expect.Call(delegate { _dao.Save(t); }).Do(delegate(Transaction t2) { t2.Message = "I drink goats blood"; });

и

Expect.Call(delegate { _dao.Save(t); }).Do(delegate { t.Message = "I drink goats blood"; });

но они дают мне

Невозможно преобразовать анонимный метод в тип System.Delegate, поскольку это не ошибка компиляции ** типа делегата.

Что я делаю неправильно?


Из-за того, что написал Марк Ингрэм, кажется, что лучший ответ, хотя никто явно этого не сказал, - это сделать следующее:

public delegate void FakeSave(Transaction t);

Expect.Call(delegate { _dao.Save(t); }).Do( new FakeSave(delegate(Transaction t2) { t.Message = expected_msg; }));

person George Mauer    schedule 12.09.2008    source источник


Ответы (4)


Это известное сообщение об ошибке. Перейдите по ссылке ниже для более подробного обсуждения.

http://staceyw1.wordpress.com/2007/12/22/they-are-anonymous-methods-not-anonymous-delegates/

По сути, вам просто нужно поставить приведение перед вашим анонимным делегатом (вашим лямбда-выражением).

Если ссылка когда-нибудь не работает, вот копия сообщения:

Это анонимные методы, а не анонимные делегаты.
Опубликовано 22 декабря 2007 г. автором staceyw1

Это не просто тема для разговора, потому что мы хотим быть сложными. Это помогает нам понять, что именно происходит. Чтобы было ясно, не существует такой вещи, как анонимный делегат. Их не существует (пока нет). Это «Анонимные методы» - точка. Важно то, как мы о них думаем и как о них говорим. Давайте посмотрим на оператор анонимного метода "delegate () {…}". На самом деле это две разные операции, и когда мы подумаем об этом так, мы больше никогда не запутаемся. Первое, что делает компилятор, это создает анонимный метод под оболочкой, используя предполагаемую сигнатуру делегата в качестве сигнатуры метода. Было бы неправильно говорить, что метод «безымянный», потому что у него есть имя, и компилятор присваивает его. Он просто скрыт от обычного просмотра. Следующее, что он делает, - это создает объект делегата требуемого типа, чтобы обернуть метод. Это называется выводом делегата и может быть источником этой путаницы. Чтобы это работало, компилятор должен уметь определять (т.е. предполагать), какой тип делегата он создаст. Это должен быть известный конкретный тип. Давайте напишем код, чтобы понять, почему.

private void MyMethod()
{
}

Не компилируется:

1) Delegate d = delegate() { };                       // Cannot convert anonymous method to type ‘System.Delegate’ because it is not a delegate type
2) Delegate d2 = MyMethod;                         // Cannot convert method group ‘MyMethod’ to non-delegate type ‘System.Delegate’
3) Delegate d3 = (WaitCallback)MyMethod;   // No overload for ‘MyMethod’ matches delegate ‘System.Threading.WaitCallback’

Строка 1 не компилируется, потому что компилятор не может определить тип делегата. Он может четко видеть желаемую подпись, но компилятор не видит конкретного типа делегата. Он мог бы создать для нас анонимный тип делегата типа, но он так не работает. Строка 2 не компилируется по той же причине. Несмотря на то, что компилятор знает сигнатуру метода, мы не даем ему тип делегата, и он не собирается просто выбирать тот, который может работать (а не то, какие побочные эффекты могут иметь место). Строка 3 не работает, потому что мы намеренно не сопоставили сигнатуру метода с делегатом, имеющим другую сигнатуру (как WaitCallback принимает и объект).

Компилирует:

4) Delegate d4 = (MethodInvoker)MyMethod;  // Works because we cast to a delegate type of the same signature.
5) Delegate d5 = (Action)delegate { };              // Works for same reason as d4.
6) Action d6 = MyMethod;                                // Delegate inference at work here. New Action delegate is created and assigned.

Напротив, они работают. Строка 1 работает, потому что мы сообщаем компилятору, какой тип делегата использовать, и они совпадают, так что это работает. Строка 5 работает по той же причине. Обратите внимание, что мы использовали специальную форму «делегат» без скобок. Компилятор определяет сигнатуру метода из приведения и создает анонимный метод с той же сигнатурой, что и предполагаемый тип делегата. Строка 6 работает, потому что MyMethod () и Action используют одну и ту же сигнатуру.

Надеюсь, это поможет.

См. Также: http://msdn.microsoft.com/msdnmag/issues/04/05/C20/

person Mark Ingram    schedule 12.09.2008
comment
Хорошая ссылка, но я до сих пор не понимаю почему компилятор не приводит ее автоматически (как в случае с лямбда-синтаксисом) - person BlueRaja - Danny Pflughoeft; 23.12.2010
comment
@BlueRaja, название сообщения должно быть подсказкой - это анонимные методы, а не анонимные делегаты. - person Mark Ingram; 12.04.2011
comment
Я скопировал текст в ответ для использования в будущем. - person Mark Ingram; 07.07.2011

Что сказал Марк.

Проблема в том, что Do принимает параметр делегата. Компилятор не может преобразовать анонимные методы в Delegate, только «тип делегата», то есть конкретный тип, производный от Delegate.

Если бы эта функция Do выполняла перегрузки Action ‹>, Action‹,> ... и т. Д., Вам не понадобилось бы приведение типов.

person hwiechers    schedule 12.09.2008

Проблема не в определении вашего делегата, а в том, что параметр метода Do () имеет тип System.Delegate, а тип делегата, созданный компилятором (FakeSave), не выполняет неявное преобразование в System.Delegate.

Попробуйте добавить трансляцию перед вашим анонимным делегатом:

Expect.Call(delegate { _dao.Save(t); }).Do((Delegate)delegate { t.Message = "I drink goats blood"; });
person Brannon    schedule 12.09.2008
comment
Это не работает делегат {} - это анонимная функция - это не тип. System.Delegate - это абстрактный базовый класс, который является неявной базой для всех объявленных типов делегатов. Здесь действительно есть 3 разных использования слова делегат! Вы можете преобразовать любой тип делегата (например, EvantHandler, Action, Func ‹R›) в Delegate, но сначала вам нужно создать тип делегата! - person George Mauer; 29.08.2009

Попробуйте что-нибудь вроде:

Expect.Call(delegate { _dao.Save(t); }).Do(new EventHandler(delegate(Transaction t2) { t2.CheckInInfo.CheckInMessage = "I drink goats blood"; }));

Обратите внимание на добавленный обработчик событий вокруг делегата.

РЕДАКТИРОВАТЬ: может не работать, поскольку сигнатуры функций EventHandler и делегата не совпадают ... Решение, которое вы добавили в конце своего вопроса, может быть единственным способом.

В качестве альтернативы вы можете создать общий тип делегата:

public delegate void UnitTestingDelegate<T>(T thing);

Так что делегат не зависит от транзакции.

person Chris Marasti-Georg    schedule 12.09.2008