или Вы могли просто записать функцию питания с рекурсией как добавленная премия
int power(int x, int y){
if(y == 0)
return 1;
return (x * power(x,y-1) );
}
да, да я знаю, что это - менее эффективная сложность пространства и времени, но рекурсия является просто большим количеством забавы!!
Run вызывает Run. Это бесконечная рекурсия.
class StackOver : MarshalByRefObject
{
public void Run()
{
Run(); // Recursive call with no termination
}
}
Единственный способ избежать переполнения стека с рекурсивными функциями - иметь четкое условие выхода, которое в конечном итоге будет выполнено независимо от ввода. Либо вы определяете максимальную глубину и прекращаете выполнение рекурсивных вызовов, как только достигнете ее, либо вы убедитесь, что исследуемые вами данные являются конечными (и в разумных пределах), либо их комбинация.
Хорошо. Неважно использовать CSharpCodeProvider или нет. Я загружаю сборку с помощью Reflection в другом домене. Думаю, домены были созданы из соображений безопасности. Как я могу защитить приложение от завершения ???
using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
class Program
{
class StackOver : MarshalByRefObject
{
public void Run()
{
Run();
}
}
static void Main(string[] args)
{
AppDomain domain = AppDomain.CreateDomain("new");
ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
if (handle != null)
{
StackOver stack = (StackOver) handle.Unwrap();
stack.Run();
}
}
}
}
Каждый раз, когда вы вызываете метод foo из панели методов, bar добавляется в стек вызовов. Стек вызовов используется для отслеживания того, где был код до вызова метода, чтобы он мог вернуться туда, когда foo будет выполнен.
Следующая рекурсивная функция
int Factorial(int n) {
if (n == 0) { return 1; }
return n * Factorial(n - 1);
}
после нескольких рекурсий вызова Factorial (5) вызов стек будет выглядеть так:
Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1)
В этот момент n равно 1, и поэтому функция перестает вызывать рекурсивный случай и вместо этого возвращает 1. Затем программа начинает откатывать стек вызовов, и все возвращается 120.
Без стека вызовов программа не знала бы, куда вернуться, когда закончит выполнение метода.
Теперь предположим, что этого базового случая не было, и он просто выглядел так:
int Factorial(int n) {
return n * Factorial(n - 1);
}
После нескольких рекурсий вызова Factorial (5) стек вызовов будет выглядеть следующим образом:
Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1) -> Factorial(0) -> Factorial(-1) -> Factorial(-2) -> Factorial(-3) -> Factorial(-4) -> Factorial(-5) -> Factorial(-6) -> Factorial(-7) -> Factorial(-8) -> Factorial(-9) -> Factorial(-10) -> Factorial(-11) -> Factorial(-12) -> Factorial(-13) -> Factorial(-14) -> Factorial(-15) etc…
Поскольку нет точки, в которой код перестает вызывать себя, он будет продолжаться вечно, а стек вызовов будет расти и расти и растут, занимая все больше и больше памяти, пока она не превысит объем выделенной памяти, и возникнет исключение StackOverflow.
Есть два способа предотвратить это, лучший зависит от ситуации.
1 Предоставьте базовый вариант. Убедитесь, что в конечном итоге достигается какое-то условие, которое не позволяет функции вызывать саму себя. В факториальном случае это n == 1, но могло случиться так, что прошло определенное количество времени, что он повторял определенное количество раз, что некоторый результат некоторых вычислений находится в каких-то границах, что угодно. Пока он прекращает повторное выполнение до того, как стек станет слишком большим.
2 Удалите рекурсию и перезапишите ее без нее. Любой рекурсивный алгоритм можно переписать как нерекурсивный алгоритм. Это может быть не так чисто и элегантно, но это можно сделать. В аргументе факториала это может быть что-то вроде:
int Factorial(int n) {
int result = 1;
for (int i = 0; i < n; i += 1) {
result *= n;
}
return result;
}
Если цель состоит в том, чтобы постоянно запускать одну и ту же функцию снова и снова, то вы можете переписать рекурсивный
void Foo() {
// Some code
Foo();
}
как
void Foo() {
while (true) { // Some code }
}
StackOverflow indicates that your recursion is going too deep and the stack is running out of memory. For example:
public class StackOver
{
public void Run()
{
Run();
}
}
This will result in a stack overflow because StackOver::Run() will be called over and over until there is no memory left.
I suspect in your case, you may be missing a termination condition or you are running too many recursion iterations.
If you are trying to keep the application running, try:
namespace TestStackOverflow
{
class Program
{
class StackOver : MarshalByRefObject
{
public bool Run()
{
return true; // Keep the application running. (Return false to quit)
}
}
static void Main(string[] args)
{
// Other code...
while (stack.Run());
}
}
}
If recursion causes a stack overflow, then the problem is not related to compiling the class -- a recursive function needs a terminating condition, because C# doesn't (usually) optimize tail calls.
I dont have a good background of CSharpCodeProvider but I know that recursively implemented algorithm could be implemented with a loop