Загрузка данных


У меня есть класс Calculator, который принимает выражение в виде строки и вычисляет результат. На строке "current.Add(new Number(Convert.ToSingle(numBuffer, CultureInfo.InvariantCulture)));" происходит ошибка: "Input string was not in a correct format.". 

Весь код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleCalculator
{

	public class Program
	{
		static public void Main()
		{
			var calc = new Calculator();
			var result = calc.Calculate("-0.2+2");
            if (result.Success) Console.WriteLine(result.Result);
            else Console.WriteLine($"Ошибка на символе {result.ErrorSymbolId}: {result.Error}");
		}

	}

	public class Calculator
	{
		public Zone? MainZone = null;
		public string Input = string.Empty;
		public CalculateResult Calculate(string input)
		{
			Input = input;
			CalculateResult result = Parse();

			if (!result.Success) // Если не удалось конвертировать
				return result;

			MainZone.InitializeZone();

			string validateResult = MainZone.Validate(null, -1);

			if (validateResult != null && validateResult != string.Empty)
				return new CalculateResult(validateResult, -1);


			MainZone.Calculate(-1);
			var numResult = MainZone.GetByIndex(0) as Number;
			return new CalculateResult(numResult.Num);
		}
		private bool isDigit(char c)
		{
			string allowedChars = "0123456789.,";

			if (allowedChars.Contains(c))
				return true;
			return false;
		}
		private CalculateResult Parse()
		{
			Zone mainZone = new Zone();
			var current = mainZone;
			string numBuffer = string.Empty;

			Input = Input.Replace(" ", "").Replace("	", "");
            Console.WriteLine(Input);

			for (int i = 0; i < Input.Length; i++)
			{
                Console.WriteLine(Input.Length);
                Console.WriteLine(i);
                Console.WriteLine(i == Input.Length -1);
				if (i == Input.Length - 1)
					Console.WriteLine("LAST1");
				char c = Input[i];
				if (isDigit(c))
				{
					numBuffer += c;
					if (i == Input.Length - 1)
                    {
						if (numBuffer != string.Empty) // Если буфер не пустой, выгружаем буфер и добавляем число в зону
						{
							current.Add(new Number(Convert.ToSingle(numBuffer, CultureInfo.InvariantCulture)));
							numBuffer = string.Empty;
						}
					}
					continue;
				}
				if (!isDigit(c))
				{
					if (numBuffer != string.Empty) // Если буфер не пустой, выгружаем буфер и добавляем число в зону
					{
						current.Add(new Number(Convert.ToSingle(numBuffer, CultureInfo.InvariantCulture)));
						numBuffer = string.Empty;
					}

				}
				switch (c)
				{
					case '(':
						var newZone = new Zone();
						current.Add(newZone);
						newZone.MyZone = current;
						current = newZone;
						break;
					case ')':
						try { current = current.MyZone; }
						catch { }
						break;
					case '+':
						current.Add(new Addition());
						break;
					case '-':
						if (i == 0 || (!isDigit(Input[i-1]) && isDigit(Input[i + 1])))
                        {
							numBuffer += '-';
							break;
                        }
						current.Add(new Difference());
						break;
					case '×':
					case '*':
						current.Add(new Multiplication());
						break;
					case '/':
						current.Add(new Division());
						break;
					case '^':
						current.Add(new Exponentiation());
						break;
					default:
						return new CalculateResult("Недопустимый символ", i);
				}
			}

			Console.WriteLine(mainZone.Count());
			Console.WriteLine(mainZone.ToString());

			MainZone = mainZone;
			return new CalculateResult(-1);
		}
	}

	public class CalculateResult
    {
		public string? Error { get; } = null;
		public int? ErrorSymbolId { get; } = null;

		public float? Result { get; } = null;
		public bool Success { get; }

		/// <summary> Создание результата с ошибкой </summary>
		public CalculateResult (string error, int errorSymbolId, Zone? mainZone = null)
        {
			Error = error;
			ErrorSymbolId = ErrorSymbolId;
			Success = false;
        }

		/// <summary> Создание успешного результата </summary>
		public CalculateResult (float result)
        {
			Result = result;
			Success = true;
        }
	}

	public abstract class CalculatableObject
	{
		public Zone MyZone = null;
		public virtual void InitializeZone(Zone zone)
		{
			MyZone = zone;
		}
	}

	public class Number : CalculatableObject
	{
		public float Num;
		public Number(float num)
		{
			Num = num;
		}
	}

	public abstract class Operator : CalculatableObject
	{
		public abstract void Calculate(int index);

		/// <summary> Возвращает null, если успешно. Возвращает строку с ошибкой, если ошибка.</summary>
		public virtual string? Validate(Zone zone, int index)
		{
			var beforeNum = zone.GetByIndex(index - 1);
			var afterNum = zone.GetByIndex(index + 1);

			if (beforeNum is null || afterNum is null) return "Оператор не может быть в конце или в начале выражения";

			// Если прошлый и следующий объект - числа или зоны(которые в будущем будут числами), значит всё верно
			if ((beforeNum is Number || beforeNum is Zone) && (afterNum is Number || afterNum is Zone)) return null; 

			return "Оператор не может производить вычисления на оператор: ";
		}
	}

	public class Zone : Operator
	{
		List<CalculatableObject> objects;
		public Zone(List<CalculatableObject> objects)
		{
			this.objects = objects;
		}
		public Zone(params CalculatableObject[] objects)
		{
			this.objects = objects.ToList();
		}
		public override void InitializeZone(Zone zone = null)
		{
			Console.WriteLine("Initialize " + objects.Count());
			if (zone != null)
				MyZone = zone;
			foreach (var obj in objects)
			{
				obj.InitializeZone(this);
			}
		}
		public override string? Validate(Zone zone, int index)
		{
			for (int i = 0; i < objects.Count; i++)
			{
				if (objects[i] is Operator op)
				{
					string validateResult = op.Validate(this, i);
					if (validateResult != null && validateResult != string.Empty)
						return validateResult;
				}
			}
			return null;
		}
		public int GetIndexOf(CalculatableObject obj)
		{
			return objects.IndexOf(obj);
		}
		public CalculatableObject? GetByIndex(int index)
		{
			if (index >= objects.Count || index < 0) return null;
			return objects[index];
		}
		public int Count() => objects.Count;
		public void Replace(CalculatableObject replaceable, CalculatableObject newObj, params CalculatableObject[] delete)
		{
			int replaceableIndex = objects.IndexOf(replaceable);

			objects[replaceableIndex] = newObj;

			foreach (var delObj in delete)
				objects.Remove(delObj);
		}
		public void Add(CalculatableObject obj, int? index = null)
		{
			if (index != null)
				objects.Insert((int)index, obj);
			else
				objects.Add(obj);
		}
		public string LocalToString()
		{
			if (MyZone != null)
				return MyZone.LocalToString();
			return ToString();
		}
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder();
			if (MyZone != null)
			{
				sb.Append('(');
			}
			foreach (var obj in objects)
			{
				if (obj is Number num)
				{
					if (num.Num < 0)
						sb.Append($"({num.Num})");
					else sb.Append(num.Num);
				}
				else if (obj is Addition)
					sb.Append('+');
				else if (obj is Difference)
					sb.Append('-');
				else if (obj is Multiplication)
					sb.Append('*');
				else if (obj is Division)
					sb.Append('/');
				else if (obj is Exponentiation)
					sb.Append('^');
				else if (obj is Zone z)
					sb.Append(z.ToString());
			}
			if (MyZone != null) sb.Append(')');
			return sb.ToString();
		}
		public override void Calculate(int index)
		{
			CalculateOperators(typeof(Zone));
			CalculateOperators(typeof(Division), typeof(Multiplication), typeof(Exponentiation));
			CalculateOperators(typeof(Addition), typeof(Difference));

			Console.WriteLine("COUNT:" + objects.Count() + " " + objects[0]);
			if (MyZone != null)
				MyZone.Replace(this, objects[0]);
			Console.WriteLine(LocalToString());
			return;
		}
		private void CalculateOperators(params Type[] types)
		{
			while (true)
			{
				bool isEnd = true;
				for (int i = 0; i < objects.Count(); i++)
				{
					var objType = objects[i].GetType();
					if (objects[i] is Operator op && types.Any(type => type == objType))
					{
						op.Calculate(i);
						Console.WriteLine(LocalToString());
						isEnd = false;
						break;
					}
				}
				if (isEnd)
					return;
			}
		}
	}
	public class Addition : Operator
	{
		public override void Calculate(int index)
		{
			var beforeObj = MyZone.GetByIndex(index - 1) as Number;
			var afterObj = MyZone.GetByIndex(index + 1) as Number;

			MyZone.Replace(this, new Number(beforeObj.Num + afterObj.Num), beforeObj, afterObj);
		}
	}
	public class Difference : Operator
	{
		public override void Calculate(int index)
		{
			var beforeObj = MyZone.GetByIndex(index - 1) as Number;
			var afterObj = MyZone.GetByIndex(index + 1) as Number;

			MyZone.Replace(this, new Number(beforeObj.Num - afterObj.Num), beforeObj, afterObj);
		}
	}
	public class Multiplication : Operator
	{
		public override void Calculate(int index)
		{
			var beforeObj = MyZone.GetByIndex(index - 1) as Number;
			var afterObj = MyZone.GetByIndex(index + 1) as Number;

			MyZone.Replace(this, new Number(beforeObj.Num * afterObj.Num), beforeObj, afterObj);
		}
	}
	public class Division : Operator
	{
		public override void Calculate(int index)
		{
			var beforeObj = MyZone.GetByIndex(index - 1) as Number;
			var afterObj = MyZone.GetByIndex(index + 1) as Number;

			MyZone.Replace(this, new Number(beforeObj.Num / afterObj.Num), beforeObj, afterObj);
		}
	}
	public class Exponentiation : Operator
	{
		public override void Calculate(int index)
		{
			Console.WriteLine("EXP");
			var beforeObj = MyZone.GetByIndex(index - 1) as Number;
			var afterObj = MyZone.GetByIndex(index + 1) as Number;

			MyZone.Replace(this, new Number((float)MathF.Pow(beforeObj.Num, afterObj.Num)), beforeObj, afterObj);
		}
	}

}