Как мне вернуть функцию делегирования или лямбда-выражение в c #?

#c# #generics #lambda

#c# #общие сведения #лямбда

Вопрос:

Я пытаюсь написать метод для возврата экземпляра самого себя. Псевдокод является

 Func<T,Func<T>> MyFunc<T>(T input)
{
    //do some work with input
    return MyFunc;
}
  

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

  which takes T as parameter, then returns a function 
 which takes T as parameter, then returns a function 
 which takes T as parameter, then returns a function

   ...recursive definition
  

Я уверен, что была какая-то тонкая вещь, которую я не заметил. Может кто-нибудь указать мне на это?
Спасибо.

Комментарии:

1. Мне интересно, почему вы пытаетесь это сделать

2. Я писал служебный метод, который является статическим внутри статического класса. Я также хочу иметь «свободную» функциональность myFunc (a). myFunc(b) … но поскольку я нахожусь внутри статического класса. Невозможно вернуть экземпляр самого класса.

Ответ №1:

Вы можете сделать это следующим образом:

 delegate F<T> F<T>(T obj);

F<T> MyFunc<T>(T obj)
{
    return MyFunc;
}
  

Но это в значительной степени бесполезно. Единственное, что вы действительно можете сделать, это что-то вроде этого, что странно:

 void Main()
{
    MyFunc(1)(2)(3)(4);
}

delegate F<T> F<T>(T obj);

F<T> MyFunc<T>(T obj)
{
    Console.WriteLine(obj);
    return MyFunc;
}
  

Комментарии:

1. Не знаю, почему я об этом не подумал … 1.

2. У меня есть блок кода проверки ошибок для каждого метода, который я внедряю. И все они очень похожи, например, if( a == null ) выдает новое исключение NullArgumentException; таким образом, действительно полезно иметь ErrorCheck(a)(b) (c) (d); Таким образом, я могу выполнить всю проверку ошибок в одной строке. Код будет намного менее загроможден. Я попробую ваше решение прямо сейчас. Будет превосходно, если это сработает 🙂

Ответ №2:

Другой способ сделать это — создать комбинатор. Проще простого, но вы не можете сделать это с помощью generics из-за этого бесконечного регресса. Вы должны объявить это напрямую:

 delegate D D(D d);
  

То есть D — это делегат, который принимает D и возвращает D.

 static D MyCombinator(D d)
{
    return MyCombinator;
}
  

Еще несколько мыслей о комбинаторах в C # здесь:

http://blogs.msdn.com/b/ericlippert/archive/2006/06/23/standard-generic-delegate-types-part-two.aspx

Ответ №3:

Это очень похоже на итератор. Если вы можете реорганизовать свой код, чтобы он был чем-то вроде:

 IEnumerator<P> GetEnumerator<T,P>(T input)
{
  while(<some condition>)
  {
     // do some work with input
     yield return results; 
  }
}
  

Где T ваш тип ввода и P ваш тип результата. Не идентично, но это должно выполнить работу.

Редактировать: Если вместо этого вы хотите свободный интерфейс, шаблон в значительной степени установлен в камне: вы создаете нестатический класс и возвращаете его из каждой функции (которая не является статической), которую вы вызываете. Нестатическая версия статического класса называется singleton, и вы можете использовать ее для этого шаблона.

Комментарии:

1. На самом деле я пытаюсь использовать этот мой служебный метод для выполнения некоторой общей задачи по реализации функции LinQ2Object, такой как Where, Select и т.д. Ваш код хорош, но это часть для «реальной» работы. Мой служебный метод предназначен для проверки ошибок перед запуском этой части.

Ответ №4:

Я не уверен, чего вы пытаетесь достичь, но в качестве интеллектуального упражнения «могу ли я вернуть метод, который возвращает сам себя?» работает следующее:

 object MyFunc<T>(T input)
{
    Func<T, object> returnValue = MyFunc;
    return returnValue;
}
  

Как вы сказали, вы не можете заставить метод возвращать a Func поскольку это означало бы, что возвращаемый тип этого делегата должен быть a, Func который возвращает a, Func который возвращает a Func и т.д…

Единственный выход из этого бесконечного повторения, который я вижу, — это заставить его возвращать object вместо этого, что требует, чтобы вызывающая сторона приводила к правильному типу.

Редактировать: ответ Поргеса лучше…

Комментарии:

1. Как я написал в ответе для porges. Я писал код проверки ошибок, который часто повторяется. Благодаря этому код становится намного более сжатым.

2. @WeiMa Альтернативой могло бы быть использование params — Лично я предпочитаю делать обработку ошибок явной. Даже если оно более подробное, оно, как правило, делает его более понятным для чтения. В любом случае это был интересный вопрос.

3. Или верните dynamic, если вы используете .NET4, чтобы избежать необходимости приводить.