تفاوت TryParse و Parse در برنامه‌نویسی C#؛ انتخاب روش امن تبدیل | راهنمای تخصصی C# و .NET

تفاوت TryParse و Parse در برنامه‌نویسی C#؛ انتخاب روش امن تبدیل

توسط admin | گروه SQL Server | 1405/05/01

نظرات 0

تفاوت TryParse و Parse در برنامه‌نویسی C#؛ انتخاب روش امن تبدیل

بازگشت به راهنمای جامع متدهای TryParse در سی‌شارپ؛ این مقاله به‌صورت مستقل تمام جنبه‌های TryParse و Parse را از سطح مقدماتی تا نکات حرفه‌ای بررسی می‌کند.

TryParse و Parse برای مقایسه TryParse و Parse در سی‌شارپ به کار می‌رود و در سناریوهایی مانند انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه مانع تبدیل خطای قابل انتظار به استثنای کنترلی می‌شود. طراحی درست فقط فراخوانی متد نیست؛ باید قرارداد ورودی، مرز نوع، فرهنگ، خطای کاربر و اعتبار دامنه نیز مشخص باشد.

دامنه یا قرارداد اصلی این نوع دو الگوی تبدیل با قرارداد خطای متفاوت است. TryParse شکست قابل انتظار را بدون استثنا گزارش می‌کند؛ Parse برای داده‌ای مناسب است که نامعتبر بودنش واقعاً خطای برنامه محسوب می‌شود. به همین دلیل نتیجه true صرفاً موفقیت تبدیل نحوی را نشان می‌دهد و مجاز بودن مقدار برای عملیات جاری باید در مرحله بعد بررسی شود.

قاعده کلیدی: مقدار result را فقط زمانی مصرف کنید که مقدار بازگشتی TryParse برابر true باشد؛ مقدار پیش‌فرض در مسیر شکست یک داده تأییدشده نیست.

تعریف، امضا و قرارداد خروجی

در الگوی TryParse، ورودی بررسی و نتیجه از طریق پارامتر out برگردانده می‌شود. TryParse یک وضعیت موفقیت و یک خروجی out می‌دهد؛ Parse مستقیماً مقدار را بازمی‌گرداند و هنگام ورودی نامعتبر استثنا پرتاب می‌کند. این قرارداد برای مرزهای غیرقابل‌اعتماد، مانند فرم و فایل، خوانا است و مسیر شکست را به بخش عادی منطق برنامه تبدیل می‌کند.

امضاهای مهم

bool ok = int.TryParse(text, out int value);
    int value = int.Parse(text, CultureInfo.InvariantCulture);
جزءنقشنکته حرفه‌ای
ورودیرشته یا Span حاوی مقدارnull، قالب و دامنه را بر اساس قرارداد بررسی کنید
resultمقدار تبدیل‌شدهفقط پس از موفقیت معتبر است
مقدار بازگشتیtrue یا falseمسیر کنترل اصلی اعتبارسنجی است
Culture و Styleقواعد تفسیردر نوع‌های حساس صریح تعیین شوند

تحلیل فنی و طراحی صحیح

استفاده حرفه‌ای از TryParse و Parse با تشخیص مرز اعتماد آغاز می‌شود. متنی که کاربر، فایل CSV، پارامتر URL یا سامانه بیرونی تولید کرده است می‌تواند خالی، ناسازگار یا مخرب باشد. TryParse یک سد نحوی فراهم می‌کند، اما طول ورودی، قواعد دامنه، مجوز دسترسی و ارتباط مقدار با سایر فیلدها همچنان باید کنترل شود.

در حوزه انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه باید میان «قابل تبدیل بودن» و «قابل پذیرش بودن» فرق گذاشت. ممکن است متن به‌درستی تبدیل شود اما مقدار خارج از بازه سیاست سازمان، شناسه ناشناخته یا تاریخ غیرمجاز باشد. این جداسازی پیام خطای دقیق‌تر، آزمون واحد ساده‌تر و تغییر قواعد کسب‌وکار بدون دستکاری Parser را ممکن می‌کند.

در API بهتر است خطای تبدیل به پاسخ ساخت‌یافته شامل نام فیلد و کد خطا تبدیل شود. در Import نیز ردیف‌های خراب را همراه شماره خط و دلیل رد در گزارش جدا نگه دارید. حذف خاموش خطاها ممکن است جمع گزارش یا تصمیم مدیریتی را تحریف کند؛ بنابراین شاخص کیفیت داده بخشی از خروجی فرایند است.

Culture باید از قرارداد واقعی داده بیاید. فایل ماشینی معمولاً با InvariantCulture و قالب ثابت امن‌تر است، درحالی‌که فرم محلی باید فرهنگ انتخاب‌شده کاربر را به کار گیرد. اتکا به CurrentCulture سرور می‌تواند پس از جابه‌جایی محیط، به‌روزرسانی کانتینر یا اجرای تست روی رایانه دیگر نتیجه را تغییر دهد.

از نظر امنیت، TryParse و Parse تنها تبدیل را انجام می‌دهد و جای اعتبارسنجی مجوز، محدودیت طول، Allowlist یا کنترل وجود رکورد را نمی‌گیرد. برای انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه ورودی بسیار طولانی را پیش از پردازش محدود کنید، اطلاعات حساس را در لاگ ننویسید و پیام داخلی استثنا را مستقیماً به کاربر نشان ندهید.

از نظر نگهداری، بهتر است فراخوانی‌های تکراری در Parser یا Value Object دامنه‌ای متمرکز شوند. آن جزء می‌تواند Culture، بازه مجاز، پیام‌های فارسی و انگلیسی و سیاست null را یکسان کند. پراکندگی تبدیل در کنترلر، فرم و Repository معمولاً به رفتارهای متفاوت و رفع اشکال دشوار منجر می‌شود.

مثال‌های عملی گام‌به‌گام

ده مثال زیر از تبدیل پایه آغاز می‌شوند و به سناریوهای مجموعه‌داده، null، مرز، خطای Culture و کارایی می‌رسند. هر مثال برای TryParse و Parse طراحی شده و نتیجه نمونه را بدون نیاز به اجرای اولیه نشان می‌دهد.

مثال شماره 1: ورودی معتبر با هر دو روش

برای داده معتبر هر دو API نتیجه یکسان می‌دهند؛ تفاوت اصلی در قرارداد گزارش شکست است.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string text = "42";
            bool ok = int.TryParse(text, out int first);
            int second = int.Parse(text);
            Console.WriteLine($"{ok}/{first}/{second}");
        }
    }
شاخص خروجینتیجه نمونه
خروجیTrue/42/42

اگر نامعتبر بودن غیرمنتظره و نشان‌دهنده باگ است، Parse می‌تواند fail-fast را شفاف کند.

مثال شماره 2: ورودی نامعتبر کاربر

TryParse شکست قابل انتظار فرم را با false برمی‌گرداند و نیاز به exception برای کنترل جریان ندارد.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string text = "چهل‌ودو";
            if (!int.TryParse(text, out int value))
            {
                Console.WriteLine("عدد معتبر وارد کنید");
            }
        }
    }
شاخص خروجینتیجه نمونه
پیامعدد معتبر وارد کنید

این انتخاب برای ورودی کاربر، فایل خارجی و API عمومی معمولاً خواناتر و کم‌هزینه‌تر است.

مثال شماره 3: مدیریت Parse با try/catch

هنگام استفاده از Parse برای داده نامطمئن باید FormatException و گاهی OverflowException مدیریت شود.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string text = "bad";
            try
            {
                int value = int.Parse(text);
                Console.WriteLine(value);
            }
            catch (FormatException)
            {
                Console.WriteLine("قالب نامعتبر");
            }
        }
    }
شاخص خروجینتیجه نمونه
نتیجهقالب نامعتبر

استثنا برای وضعیت استثنایی مناسب است؛ استفاده از آن در میلیون‌ها شکست قابل انتظار هزینه و نویز پایش ایجاد می‌کند.

مثال شماره 4: تفاوت null

Parse روی null برای انواع عددی ArgumentNullException ایجاد می‌کند، درحالی‌که TryParse false می‌دهد.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string? text = null;
            Console.WriteLine(int.TryParse(text, out _));
            try
            {
                _ = int.Parse(text!);
            }
            catch (ArgumentNullException)
            {
                Console.WriteLine("ArgumentNullException");
            }
        }
    }
شاخص خروجینتیجه نمونه
TryParseFalse
ParseArgumentNullException

قرارداد nullable مدل را روشن کنید و صرفاً به رفتار تبدیل تکیه نکنید.

مثال شماره 5: سرریز عدد

ورودی از محدوده int خارج است. TryParse false و Parse در صورت اجرا OverflowException تولید می‌کند.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string text = "2147483648";
            Console.WriteLine(int.TryParse(text, out _));
            try
            {
                _ = int.Parse(text);
            }
            catch (OverflowException)
            {
                Console.WriteLine("OverflowException");
            }
        }
    }
شاخص خروجینتیجه نمونه
TryParseFalse
ParseOverflowException

نوع مقصد باید با دامنه واقعی انتخاب شود؛ تغییر به long تنها زمانی درست است که دامنه کسب‌وکار آن را می‌پذیرد.

مثال شماره 6: Culture صریح در هر دو API

هر دو خانواده overloadهایی برای Culture و NumberStyles دارند. انتخاب TryParse جای قرارداد فرهنگ را نمی‌گیرد.

using System;
    using Globalization;

    internal static class Program
    {
        private static void Main()
        {
            string text = "1.234,56";
            CultureInfo culture = CultureInfo.GetCultureInfo("de-DE");
            bool ok = decimal.TryParse(text, NumberStyles.Number, culture, out decimal a);
            decimal b = decimal.Parse(text, NumberStyles.Number, culture);
            Console.WriteLine($"{ok}/{a}/{b}");
        }
    }
شاخص خروجینتیجه نمونه
نتیجههر دو 1234.56

فرهنگ را از قرارداد ورودی بگیرید، نه از ماشین میزبان؛ این اصل برای هر دو API یکسان است.

مثال شماره 7: تاریخ خارجی و تاریخ داخلی

ورودی خارجی با TryParse بررسی می‌شود، اما یک ثابت داخلی ISO که خرابی آن باگ است می‌تواند با Parse خوانده شود.

using System;
    using Globalization;

    internal static class Program
    {
        private static void Main()
        {
            string external = "2026-07-23";
            if (DateTime.TryParse(external, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime received))
            {
                DateTime internalDate = DateTime.Parse("2026-01-01", CultureInfo.InvariantCulture);
                Console.WriteLine(received >= internalDate);
            }
        }
    }
شاخص خروجینتیجه نمونه
شرطTrue

مرز اعتماد داده معیار بهتری از سلیقه شخصی برای انتخاب API است.

مثال شماره 8: Guid در API

شناسه مسیر HTTP نامطمئن است و TryParse پاسخ 400 را بدون استثنای غیرضروری ممکن می‌کند؛ ثابت توسعه‌دهنده با Parse قابل قبول است.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string routeValue = "bad-guid";
            if (!Guid.TryParse(routeValue, out Guid id))
            {
                Console.WriteLine("400 Bad Request");
            }
            Guid known = Guid.Parse("6f9619ff-8b86-d011-b42d-00c04fc964ff");
            Console.WriteLine(known != Guid.Empty);
        }
    }
شاخص خروجینتیجه نمونه
ورودی مسیر400 Bad Request
ثابت داخلیمعتبر

Parse برای ثابت نامعتبر در تست یا شروع برنامه سریعاً باگ را آشکار می‌کند.

مثال شماره 9: Pipeline پردازش انبوه

TryParse به‌راحتی در حلقه نتیجه‌های خراب را شمارش می‌کند و عملیات سالم را ادامه می‌دهد.

using System;

    internal static class Program
    {
        private static void Main()
        {
            string[] raw = { "10", "bad", "20", "30" };
            int total = 0;
            int rejected = 0;
            foreach (string text in raw)
            {
                if (int.TryParse(text, out int value)) total += value;
                else rejected++;
            }
            Console.WriteLine($"total={total}; rejected={rejected}");
        }
    }
شاخص خروجینتیجه نمونه
مجموع60
ردشده1

کنار نتیجه تجمیعی، شاخص کیفیت داده را نیز نمایش دهید تا حذف رکوردها پنهان نماند.

مثال شماره 10: مقایسه هزینه شکست پرتکرار

یک حلقه شکست‌های قابل انتظار را با TryParse پردازش می‌کند. نسخه Parse نیازمند ساخت و گرفتن استثنا در هر تکرار بود.

using System;
    using Diagnostics;
    using Linq;

    internal static class Program
    {
        private static void Main()
        {
            string[] raw = Enumerable.Repeat("bad", 10_000).ToArray();
            Stopwatch watch = Stopwatch.StartNew();
            int failures = 0;
            foreach (string text in raw)
            {
                if (!int.TryParse(text, out _)) failures++;
            }
            watch.Stop();
            Console.WriteLine($"failures={failures}; elapsed={watch.ElapsedMilliseconds}ms");
        }
    }
شاخص خروجینتیجه نمونه
شکست مدیریت‌شده10000
استثنای کنترلی0

عدد زمان به سخت‌افزار وابسته است و این نمونه Benchmark علمی نیست. برای تصمیم کارایی از BenchmarkDotNet و داده واقعی استفاده کنید.

خطاهای رایج و راه اصلاح

  • استفاده از result بدون بررسی مقدار بازگشتی؛ اصلاح: منطق مصرف مقدار را داخل شاخه موفقیت قرار دهید.
  • یکی گرفتن مقدار پیش‌فرض با شکست؛ اصلاح: وضعیت تبدیل را همراه مقدار نگه دارید یا Result صریح بسازید.
  • اتکا به فرهنگ سرور؛ اصلاح: Culture و Style را از قرارداد داده تعیین کنید.
  • پذیرش هر مقدار قابل تبدیل؛ اصلاح: پس از تبدیل، بازه، Allowlist و قواعد ارتباطی دامنه را بررسی کنید.
  • حذف خاموش رکورد خراب در Import؛ اصلاح: شمارش و گزارش خطا را در خروجی فرایند قرار دهید.
  • نادیده گرفتن نکته اختصاصی نوع؛ اصلاح: TryParse شکست قابل انتظار را بدون استثنا گزارش می‌کند؛ Parse برای داده‌ای مناسب است که نامعتبر بودنش واقعاً خطای برنامه محسوب می‌شود.

پیام خطا باید قابل اقدام باشد. به‌جای «ورودی نامعتبر است» می‌توان قالب مورد انتظار، محدوده مجاز و یک نمونه صحیح را ارائه کرد. بااین‌حال جزئیات داخلی، Stack Trace یا داده حساس نباید در پاسخ عمومی ظاهر شود.

کارایی، تخصیص حافظه و Best Practice

TryParse برای شکست قابل انتظار از الگوی Parse همراه try/catch مناسب‌تر است، زیرا استثنا برای کنترل جریان عادی طراحی نشده است. این حکم به معنی ممنوع بودن Parse نیست؛ اگر رشته یک ثابت داخلی معتبر است و خرابی آن باگ برنامه محسوب می‌شود، fail-fast با Parse می‌تواند انتخاب روشنی باشد.

overloadهای ReadOnlySpan در Parserهای پرتکرار امکان کار روی برشی از بافر را بدون ساخت substring می‌دهند. سود آن در فایل‌های بزرگ، پروتکل یا پردازش میلیون‌ها توکن بیشتر است. در فرم عادی، پیچیدگی اضافی ممکن است ارزش نداشته باشد؛ ابتدا CPU، Allocation و نرخ شکست را اندازه‌گیری کنید.

برای آزمون، حداقل حالت‌های معتبر، null، خالی، فاصله، Culture متفاوت، کمینه، بیشینه، خارج از محدوده و مقدار معتبر ولی غیرمجاز را پوشش دهید. تست‌ها باید Target Framework واقعی را اجرا کنند، زیرا برخی overloadها و رفتارهای گوشه‌ای میان نسخه‌های .NET تفاوت دارند.

سؤالات متداول

پرسش 1: TryParse و Parse دقیقاً چه مسئله‌ای را حل می‌کند؟

TryParse و Parse متن را با قرارداد نوع مقصد بررسی می‌کند و بدون تبدیل شکست قابل انتظار به استثنا، وضعیت موفقیت را برمی‌گرداند. این ویژگی در ورودی کاربر، فایل، API و تنظیمات ارزشمند است. حوزه رایج این مقاله انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه است، اما اعتبار نحوی فقط نخستین مرحله است و قواعد دامنه باید جدا اعمال شوند.

پرسش 2: در صورت شکست TryParse و Parse چه اتفاقی برای result می‌افتد؟

متد false برمی‌گرداند و result مقدار پیش‌فرض نوع را خواهد داشت یا نباید معتبر فرض شود. الگوی درست این است که result فقط در شاخه true مصرف شود. تکیه بر صفر، false، تاریخ کمینه یا Guid.Empty می‌تواند داده نامعتبر را با یک مقدار واقعی اشتباه بگیرد و خطای خاموش ایجاد کند.

پرسش 3: Culture چه اثری بر TryParse و Parse دارد؟

اثر Culture به نوع بستگی دارد. اعداد اعشاری، جداکننده هزارگان و تاریخ به فرهنگ حساس‌اند، درحالی‌که Guid و bool قرارداد محدودتری دارند. در مرزهای ماشینی CultureInfo.InvariantCulture و قالب مستند، و در رابط کاربر فرهنگ انتخاب‌شده کاربر مناسب است. فرهنگ سرور نباید به‌طور تصادفی نتیجه را تعیین کند.

پرسش 4: چگونه TryParse و Parse را در نرم‌افزار تجاری استفاده کنیم؟

یک لایه اعتبارسنجی بسازید که متن خام، نتیجه تبدیل، کد خطا و پیام قابل فهم را برگرداند. سپس قانون کسب‌وکار مربوط به انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه را اعمال کنید. این جداسازی هم تست‌پذیری را بالا می‌برد و هم امکان گزارش کیفیت داده، مشاوره فنی و توسعه سفارشی جریان Import را فراهم می‌کند.

پرسش 5: آیا جایگزینی بهتر از TryParse و Parse وجود دارد؟

پاسخ به قرارداد ورودی وابسته است. برای قالب ثابت TryParseExact، برای داده‌ای که خرابی آن باگ است Parse، و برای تبدیل‌های سفارشی یک Value Object یا Parser دامنه‌ای مناسب‌تر است. هدف انتخاب کوتاه‌ترین کد نیست؛ باید خطا، Culture، سازگاری نسخه و پیام کاربر به‌روشنی مدیریت شود.

پرسش 6: برای پیاده‌سازی سازمانی TryParse و Parse چه خدماتی لازم می‌شود؟

در پروژه بزرگ معمولاً طراحی قرارداد داده، اعتبارسنجی چندلایه، تست مرزی، ثبت خطای امن، داشبورد کیفیت ورودی و پایش لازم است. تیم توسعه یا مشاور می‌تواند این اجزا را متناسب با معماری C# و .NET، حجم داده و الزامات امنیتی پیاده کند. خود متد کوچک است، اما فرایند قابل اعتماد پیرامون آن اهمیت اصلی را دارد.

پرسش 7: خطای رایج هنگام کار با TryParse و Parse چیست؟

رایج‌ترین خطا استفاده از result بدون بررسی مقدار بازگشتی است. خطاهای دیگر شامل اتکا به Culture جاری، نادیده گرفتن مرز نوع، یکی گرفتن اعتبار نحوی و معنایی و حذف خاموش رکورد نامعتبر است. نکته اختصاصی این موضوع نیز چنین است: TryParse شکست قابل انتظار را بدون استثنا گزارش می‌کند؛ Parse برای داده‌ای مناسب است که نامعتبر بودنش واقعاً خطای برنامه محسوب می‌شود.

پرسش 8: کارایی TryParse و Parse در پردازش حجیم چگونه است؟

در شکست‌های قابل انتظار، TryParse معمولاً از کنترل جریان با exception مناسب‌تر است. overloadهای ReadOnlySpan می‌توانند تخصیص substring را کاهش دهند. بااین‌حال نتیجه به نسخه .NET، الگوی داده و مسیر اجرا وابسته است؛ پروفایلر و BenchmarkDotNet باید تصمیم را تأیید کنند و بهینه‌سازی زودهنگام نباید خوانایی را قربانی کند.

پرسش 9: بهترین روش طراحی با TryParse و Parse چیست؟

قرارداد ورودی را مستند کنید، Culture و Style را در صورت نیاز صریح بدهید، result را فقط پس از true مصرف کنید، اعتبار دامنه را جدا بسنجید و خطا را با پیام قابل اقدام گزارش دهید. برای انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه آزمون‌های مرزی، null، مقدار نامعتبر، ورودی بسیار بزرگ و سناریوی واقعی کسب‌وکار را در مجموعه تست قرار دهید.

پرسش 10: TryParse و Parse با کدام نسخه‌های C# و .NET سازگار است؟

overload پایه string در نسخه‌های قدیمی .NET نیز در دسترس است، اما overloadهای Span، UTF-8 و رابط‌های Generic Math در نسخه‌های جدیدتر افزوده شده‌اند. پروژه باید مستندات Target Framework خود را مبنا قرار دهد. اگر کتابخانه چندهدفه است، API سطح مشترک را انتخاب یا برای نسخه‌های جدید مسیر بهینه جدا تعریف کنید.

سؤالات مصاحبه C# و .NET

پرسش‌های زیر دانش قراردادی و معماری داوطلب را می‌سنجند، نه صرفاً حفظ کردن امضای متد.

  1. تفاوت قرارداد خطای TryParse و Parse با Parse را توضیح دهید و برای هر کدام یک سناریو نام ببرید.
  2. چرا بررسی مقدار بازگشتی قبل از result ضروری است و مقدار پیش‌فرض چه خطری دارد؟
  3. اثر Culture، Style یا قالب ورودی بر TryParse و Parse را با یک مثال تشریح کنید.
  4. اعتبار نحوی و اعتبار کسب‌وکار در موضوع انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه چگونه از هم جدا می‌شوند؟
  5. چه زمانی overload مبتنی بر ReadOnlySpan ارزشمند است و چگونه سود آن را اندازه می‌گیرید؟

چک‌لیست نهایی

  • نوع مقصد و محدوده آن با دامنه واقعی داده سازگار است.
  • Culture، Style یا قالب ثابت در مرز سیستم مشخص شده است.
  • result فقط در مسیر موفقیت مصرف می‌شود.
  • اعتبار نحوی از قواعد کسب‌وکار جداست.
  • null، مقدار خالی، مرز و ورودی خراب تست شده‌اند.
  • خطاها قابل پایش‌اند و داده حساس در لاگ قرار نمی‌گیرد.
  • بهینه‌سازی Span تنها پس از اندازه‌گیری انجام شده است.

جمع‌بندی

TryParse و Parse ابزار کوچکی برای ساخت مرز ورودی قابل اعتماد است. ارزش واقعی آن زمانی دیده می‌شود که قرارداد فرهنگ، محدوده، گزارش خطا و اعتبار دامنه پیرامونش درست طراحی شود. در انتخاب راهبرد تبدیل برای ورودی کاربر، داده داخلی، فایل، API و پردازش انبوه همین رویکرد از داده خاموش اشتباه، استثناهای پرتکرار و اختلاف رفتار محیط‌ها جلوگیری می‌کند.

برای مرور سایر نوع‌ها و انتخاب API مناسب، راهنمای جامع همه متدهای TryParse در C# و .NET را مطالعه کنید.

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

حرف 500 حداکثر