مرجع تخصصی برنامه نویسان

انجمن تخصصی برنامه نویسان فارسی زبان

کاربر سایت

plastic

عضویت از 1393/08/19

فرمول نویسی در textBox

  • شنبه 17 آبان 1399
  • 12:07
تشکر میکنم

سلام وقت بخیر.  

خسته نباشید. 

بنده قصد دارم که داخل یک textbox فرمولی رو بنویسم . و بعد از زدن کلید محاسبه مقادیر فرمول را محاسبه کند . مثلا 

یک سری دیتا در دیتاگرید ویو دارم به شکل : 

a = 200

b = 20

c = 2

سپس در یک تکس باکس اگر عبارت (a^2+c) ضرب در b رو نوشتم میخوام محاسبه با توجه به اولویت بندی انجام بشه . یا اگر فرمول را به هر سبکی تغییر دادم خروجی به درستی محاسبه بشه . 

خیلی در سایت های خارجی سرچ کردم و حتی سایت های داخلی  متاسفانه مطلبی که کار منو بخواد راه بندازه رو پیدا نکردم . ممنون میشم روش و منطق چگونگی این کار رو بهم بگید . 

سپاس . 

 

 

پاسخ های این پرسش

تعداد پاسخ ها : 6 پاسخ
کاربر سایت

بهزاد

عضویت از 1399/08/05

  • شنبه 17 آبان 1399
  • 17:46

سلام 

میتونی به کمک Reflection و namespace های CSharp و CodeDom.Compiler و یه کد استرینگ یه کلاس که جنریتش کردی، کد رو تو Runtime اجرا کنی و ازش خروجی بگیری

مثلا تو کد زیر فرض کن cells مقادیر دیتای تو گرید ویوت هست و textBox متن داخل تکست باکس ات هست ... اگه کد زیر رو اجرا کنی خروجی 4080 رو میگیری که همون result هستش 

using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;

namespace Console_Test
{
    class Program
    {
        static void Main(string[] args)
		{

			var cells = new string[]
			{
				"a = 200",
				"b = 20",
				"c = 2"
			};

			var textBox = "(a^2+c)*b";

			var generateFormula = "";

			foreach (var item in cells)
            {
				generateFormula += "var " + item + "; ";
			}
			generateFormula += "return " + textBox + "; ";

			var source = string.Format("using System; namespace Onfly {{ public class FormulaCalculator {{ public object Calculate() {{ {0} }} }} }}", generateFormula);

			var provider = new CSharpCodeProvider();

			var compilerParams = new CompilerParameters
			{
				GenerateInMemory = true,
				GenerateExecutable = false
			};

			var compilerResults = provider.CompileAssemblyFromSource(compilerParams, source);

			var codeInstance = compilerResults.CompiledAssembly.CreateInstance("Onfly.FormulaCalculator");

			var result = codeInstance.GetType().GetMethod("Calculate").Invoke(codeInstance, null).ToString();

			Console.WriteLine(result);
			Console.ReadKey();
		}
    }
}

 

کاربر سایت

بهزاد

عضویت از 1399/08/05

  • شنبه 17 آبان 1399
  • 18:07

و بهتره بعد از گرفتن خروجی provider رو Dispose کنی یا از using استفاده کنی

using (var provider = new CSharpCodeProvider())
{ 
	var compilerParams = new CompilerParameters
	{
		GenerateInMemory = true,
		GenerateExecutable = false
	};

	var compilerResults = provider.CompileAssemblyFromSource(compilerParams, source);
	var codeInstance = compilerResults.CompiledAssembly.CreateInstance("Onfly.FormulaCalculator");
	var result = codeInstance.GetType().GetMethod("Calculate").Invoke(codeInstance, null).ToString();

	Console.WriteLine(result);
}
کاربر سایت

رضا نصیری

عضویت از 1392/10/01

  • یکشنبه 18 آبان 1399
  • 09:51

سلام 

 public static long? Calculate(string str)
        {
            try
            {
                DataTable dt = new DataTable();
                return Convert.ToInt64(dt.Compute(str, "").ToString());
            }
            catch (Exception)
            {
                return null;
            }
        }

 

کافیه فرمول رو به متد بدی برات نتیجه رو برگردونه

 

 

کاربر سایت

بهزاد

عضویت از 1399/08/05

  • یکشنبه 18 آبان 1399
  • 12:45

سلام جناب نصیری عزیز

 

جسارتا شما کدی که زحمت کشیدین و گذاشتین رو تست کردین با فرمول و دیتایی که دوستمون گذاشتن؟! 

Compute ستون های با مقادیر عددی رو محاسبه میکنه ولی ایشون توی دیتاشون a و b و c رو بصورت رشته ای مقدار دهی کردن!

شاید هم من اشتباه متوجه شدم سوالشون رو

 

کاربر سایت

plastic

عضویت از 1393/08/19

  • یکشنبه 18 آبان 1399
  • 13:56

 

سلام 

میتونی به کمک Reflection و namespace های CSharp و CodeDom.Compiler و یه کد استرینگ یه کلاس که جنریتش کردی، کد رو تو Runtime اجرا کنی و ازش خروجی بگیری

مثلا تو کد زیر فرض کن cells مقادیر دیتای تو گرید ویوت هست و textBox متن داخل تکست باکس ات هست ... اگه کد زیر رو اجرا کنی خروجی 4080 رو میگیری که همون result هستش 

 

 

جناب بهزاد عزیز و اقای نصیری بزرگوار ، درود . 

با تشکر از لطف شما . 

جناب نصیری خدمت شما عرض کنم که فرمول ثابت نیست و ممکن است به هر شکلی تبدیل شود و با هر تعداد ورودی . مثلا کاربر مشخص میکند که سیستم 5 نوع متغییر داشته باشد . و خود کاربر فرمول را هم مینویسد . سیستم باید نتیجه فرمول را بدرستی محاسبه کند . 

 

جناب بهزاد عزیز . با تشکر از کدی که زحمت کشیدید . فقط چند سوال : 

در  generateFormula  دقیقا چه متغییر هایی را قرار میدهید  ؟ 

کلا نحوه نوشتن متغییر source که از نوع استرینگ هست رو متوجه نشدم . 

در مورد compilerParams  و provider هم اگر امکان داشت توضیح بدید . سپاس فراوان 

کاربر سایت

بهزاد

عضویت از 1399/08/05

  • یکشنبه 18 آبان 1399
  • 15:09

سلام مجدد

CSharpCodeProvider کلاسی هست که میتونین با اون یه کد سی شارپ رو در زمان Runtime تولید و کامپایل کنید ... که اینجا provider هست

این کلاس یه متدی داره به اسم CompileAssemblyFromSource که دوتا پارامتر داره! اولیش از نوع کلاس CompilerParameter هست که پارامتر های کامپایل شدن کد سی شارپی رو که میخواییم کامپایل کنیم رو مشخص میکنیم که تو این کد گفتم:

 میخواییم کامپایلر یه کدی رو تو حافظه تولید کنه -  GenerateInMemory = true - 

و لازم نیست فایل خروجی (dll- exe...) داشته باشه -  GenerateExecutable = false -  

پارامتر دوم CompileAssemblyFromSource سورس کد سی شارپ هستش ... ( که میشه کد زیر )

 * وقتی تو ویژوال استادیو یه کدی رو داریم Debug میکنیم و اجرا میکنیم تقریبا همین اتفاق می افته!

حالا فرض کنید در حالت عادی اگه بخواییم تو C# این فرمول رو مقادیر رو محاسبه کنیم یه همچین کلاسی درست کنیم به اسم  FormulaCalculator  و یه متد داشته باشه به اسم Calculate که توش سه تا متغییر a, b , c وجود داره و در آخر متد فرمول رو محاسبه میکنه و نتیجه رو برمیگردونه! ( میتونه مقدار برگشتی عددی باشه ! ولی من object گذاشتم که ساده تر باشه و کار تبدیل رو c# انجام بده )

کدمون یه چیزی میشه شبیه به این: 

using System; 
namespace Onfly 
{ 
    public class FormulaCalculator 
    { 
        public object Calculate() 
        { 
             var a = 200;
             var b = 20;
             var c = 20;
             return (a+2+c)*b;
        }
    }
}

حالا به فرض اینکه دیتاویو شما یه ستون داره که رشته هست نوعش و داخلش هم این مقادیر (Row) باشه 

a=200

b=20

c=2

با هر روشی که مد نظرتون هست میاییم اول یه این مقادیر رو خط به خط میخونیم و یه var به اول و یه ; به آخر هر سطر اضافه میکنیم  به یه متغیر رشته ای اضافه میکنیم که من با foreach کردم و خروجی رو ریختم تو متغیر رشته ای generatedFormula

تا اینجا مقدار generatedFormula شد:

var a=200; var b=20; var c=2;

بعد فرمول رو بهش یه return در ابتدا و یه ; انتها اضافه میکنیم و کلش رو به generatedFormula اضافه میکنیم که تا اینجا مقدار generatedFormula میشه:

var a=200; var b=20; var c=2; return (a+2+c)*b;

حالا میاییم میخواییم کل کلاس اولی رو که نوشتم درست کنیم باید از using  شروع کنیم و تا آخر

من اومد یه متغیر رشته ای دیگه ایجاد کردم به اسم source و مقدار source میشه خطوط ابتدایی کد اولمون به علاوه generatedFormula به علاوه آکولاد های بستن بلاک متد و کلاس و namespace 

"using System; namespace Onfly {     public class FormulaCalculator     {  public object Calculate()  { "
+
"var a=200; var b=20; var c=2; return (a+2+c)*b;

"
+
" } } }"

که مقدار source در آخر میشه همون کد اول تو یه خط :

using System; namespace Onfly {     public class FormulaCalculator     {  public object Calculate()  { var a=200; var b=20; var c=2; return (a+2+c)*b;

 } } }

حالا با اجرای متد CompileAssemblyFromSource از کلاس CSharpCodeProvider و پارامتر های لازمش که ایجاد کردیم - compilerParams و source ) یه خروجی کامپایل شده از source تو حافظه داریم

هر Assembly یک متد داره به اسم CreateInstance تقریبا کار new رو میکنه 

var fc = new FormulaCalculator()

حالا میایم از کلاس FormulaCalculator با کمک این متد یه Instance جدید میسازیم و  با کمک Reflection میایم متد Calculate کلاس رو با استفاده از Invoke صدا میزنیم و خروجی میگیریم

 

اگه چیزی رو نامفهوم و ناقص توضیح دادم حتما بگید ...

کاربرانی که از این پست تشکر کرده اند

هیچ کاربری تا کنون از این پست تشکر نکرده است

اگر نیاز به یک مشاور در زمینه طراحی سایت ، برنامه نویسی و بازاریابی الکترونیکی دارید

با ما تماس بگیرید تا در این مسیر همراهتان باشیم :)