2013-11-30 2 views
0

В принципе, я хочу делегата, который может вернуться сам. У меня есть очень простое сокращение того, что я теоретически хочу ниже:C# делегат, который возвращает себя

int i =0; 
var x =() => 
{ 
    if (i > 10) 
     return null; 

    ++i; 
    Debug.Log("keep going"); 
    return x; 
}; 

var y = x; 

while ((y = y()) != null) ; 

Ожидаемый результат конечно видит «будет держать» 10 раз в консоли. Я просто хочу знать, возможно ли это или нет на этом языке (я знаю, что могу достичь той же цели с циклом for). Например, тривиально это сделать в JavaScript, используя очень похожий код, как описано выше, но я не могу заставить компилятор сотрудничать с C#. Я думаю, что неудача заключается в невозможности правильно описать тип для «x» (Func<Func<Func..., вы вроде бы хотите что-то вроде прямого объявления).

+0

вы пробовали это ваш сам –

+0

Есть ли реальный мир случай, когда этот вид кода может быть полезным? –

+0

@JulieShannon Да, я пробовал точный код выше, и он жалуется на «var» (возможно, я должен упомянуть, что я запускаю Mono в Unity (совместимость с .NET 2), поэтому, возможно, приведенный выше код совершенно легален в более новых версиях C# –

ответ

5

Чтобы прояснить мой комментарий из ранее, C# использует статическую типизацию и «reified» generics. Это означает, что компилятор отслеживает каждую параметризацию родового типа. Тип Func<Func<int>> отличается от типа Func<Func<Func<int>>>. C# не поддерживает никакой динамической рекурсии типов, не говоря уже о бесконечной параметризации, а наивная конструкция типа Func<Func<... будет включать в себя бесконечную рекурсию, которая проявляется в необходимости отслеживать бесконечное количество типов.

Однако есть несколько трюков, которые могут заставить его работать. Все они связаны каким-то образом скрывают возвращаемый тип функции. Другие решения делают это, возвращая слабо типизированный, поздний связанный объект Delegate. Однако существует также статически типизированное решение. Все, что вам нужно, это определить свой собственный, не общий объект delegate.

private delegate InfFunc InfFunc(); 
    static void Main(string[] args) 
    { 
     InfFunc f = null; 
     int i = 0; 
     f =() => 
      { 
       if (i > 10) return null; 
       i++; 
       Debug.WriteLine("Keep going"); 
       return f; 
      }; 
     var g = f; 

     while ((g = g()) != null) ; 
     Debug.WriteLine(i); 
    } 

Или вы можете сделать это, хотя ваша версия .NET не поддерживает DLR:

static void Main(string[] args) 
    { 
     Func<dynamic> f = null; 
     int i = 0; 
     f =() => 
      { 
       if (i > 10) return null; 
       i++; 
       Debug.WriteLine("Keep going"); 
       return f; 
      }; 
     var g = f; 

     while ((g = g()) != null) ; 
     Debug.WriteLine(i); 
    } 
+0

Ах, блестящий, большое спасибо! RE: второй пример, Func , казалось, работал просто отлично (then doing = (Func ) (f())). Но да, первый пример был именно тем, что я хотел. –

1

Возможно, не совсем то, что вы надеетесь, но это будет делать трюк:

 int i = 0; 
     Func<Delegate> x = null; 
     x =() => 
     { 
      if (i > 10) 
       return null; 

      Debug.Log("keep going"); 
      ++i; 
      return x; 
     }; 
     Func<Delegate> y = x; 
     while ((y = y() as Func<Delegate>) != null) ; 

Для того, чтобы решить проблему, связанную с бесконечной последовательности Func<Func<... вы можете использовать Delegate и затем отливали что Func<Delegate> после вызова.

+0

Кажется, что только цикл один раз (с использованием Mono с совместимостью .NET 2 в Unity - извинения, если это ведет себя по-разному в более новых версиях C#) –

+0

Извините, неправильно прочитайте вывод, это на самом деле отлично работает. Я выбрал другой подобный ответ только потому, что он был раньше, но я все еще давал вам голосование. –

+0

Это хорошо, но вы должны знать, что 'DynamicInvoke' вводит дополнительные накладные расходы, поэтому я выбрал актерский состав. –

0

Я не уверен, о том, что вы пытаетесь сделать, но:

int i =0; 
Func<Delegate> x = null; 
x =() => 
{ 
    if (i > 10) 
     return null; 

    ++i; 
    Console.WriteLine("keep going"); 
    return x; 
}; 

var y = x; 

while (y() != null) ; 
+0

y = y() имеет решающее значение, поскольку y() может потенциально возвращать другую функцию. например, если (i> 10) return() => {Debug.Log («последний»); return null}; Извиняюсь за то, что он не стал более ясным. –

1

Я думаю, что лучшее, что вы можете сделать, это сделать тип возвращаемого Delegate:

int i = 0; 
Func<Delegate> f = null; 
f =() => 
{ 
    if (i > 10) 
     return null; 

    ++i; 
    Debug.WriteLine("keep going"); 

    return f; 
}; 

Delegate y = f; 

while ((y = (Delegate)y.DynamicInvoke()) != null) 
{ 

} 
+0

Кажется, что это только цикл один раз (с использованием Mono с совместимостью .NET 2 в Unity - извинения, если это ведет себя по-другому в более новых версиях C#) –

+0

Извините, что я неправильно читал вывод, это действительно отлично работает. Благодаря! –

Смежные вопросы