در طول تاریخ خود ، . NET سعی کرده است سطح بالایی از سازگاری را از نسخه به نسخه به نسخه و در سراسر اجرای . NET حفظ کند. اگرچه . NET 5 (و . NET CORE) و نسخه های بعدی می توانند به عنوان یک فناوری جدید در مقایسه با چارچوب . NET در نظر گرفته شوند ، دو عامل اصلی توانایی این اجرای . NET را برای واگرایی از چارچوب . NET محدود می کنند:
تعداد زیادی از توسعه دهندگان در ابتدا توسعه یافته یا به توسعه برنامه های . NET Framework ادامه می دهند. آنها انتظار رفتار مداوم را در سراسر اجرای . NET دارند.
پروژه های کتابخانه استاندارد . NET به توسعه دهندگان اجازه می دهد تا کتابخانه هایی را ایجاد کنند که API های مشترک را که توسط . NET Framework و . NET 5 (و . NET Core) و نسخه های بعدی به اشتراک گذاشته شده اند ، هدف قرار دهند. توسعه دهندگان انتظار دارند که کتابخانه ای که در یک برنامه . NET 5 استفاده می شود ، به طور یکسان با همان کتابخانه مورد استفاده در یک برنامه . NET Framework رفتار کند.
همراه با سازگاری در سراسر پیاده سازی های . NET ، توسعه دهندگان انتظار دارند سطح بالایی از سازگاری در سراسر نسخه های اجرای داده شده از . NET. به طور خاص ، کدی که برای نسخه قبلی . NET Core نوشته شده است باید یکپارچه روی نسخه . NET 5 یا نسخه بعدی اجرا شود. در حقیقت ، بسیاری از توسعه دهندگان انتظار دارند که API های جدید موجود در نسخه های تازه منتشر شده از . NET نیز باید با نسخه های پیش از انتشار که در آن API ها معرفی شده اند سازگار باشد.
در این مقاله تغییراتی که بر سازگاری تأثیر می گذارد و نحوه ارزیابی تیم . NET هر نوع تغییر را بیان می کند. درک چگونگی نزدیک شدن تیم . NET به تغییرات احتمالی شکستن به ویژه برای توسعه دهندگان که درخواست های کشش را باز می کنند که رفتار API های NET موجود را تغییر می دهد ، مفید است.
در بخش های زیر مقوله های تغییرات ایجاد شده در API های NET و تأثیر آنها در سازگاری کاربردها شرح داده شده است. تغییرات یا مجاز هستند ، مجازات نشده ، یا نیاز به قضاوت و ارزیابی چگونگی پیش بینی ، آشکار و سازگار رفتار قبلی داشتند.
- علاوه بر خدمت به عنوان راهنمایی برای چگونگی ارزیابی تغییرات در کتابخانه های . NET ، توسعه دهندگان کتابخانه همچنین می توانند از این معیارها برای ارزیابی تغییرات در کتابخانه های خود استفاده کنند که چندین اجرای و نسخه های NET را هدف قرار می دهند.
- برای کسب اطلاعات در مورد دسته های سازگاری ، به عنوان مثال ، سازگاری رو به جلو و عقب ، به سازگاری مراجعه کنید.
اصلاحات در قرارداد عمومی
تغییرات در این گروه ، سطح عمومی از یک نوع را تغییر می دهد. بیشتر تغییرات در این گروه از آنجا که آنها سازگاری به عقب را نقض می کنند ، مجاز نیست (توانایی برنامه ای که با نسخه قبلی یک API برای اجرای بدون بازپرداخت در نسخه بعدی تهیه شده است).
انواع
✔ مجاز: حذف یک رابط از یک نوع هنگامی که رابط توسط یک نوع پایه اجرا شده است
❓ نیاز به قضاوت دارد: اضافه کردن اجرای رابط جدید به یک نوع
این یک تغییر قابل قبول است زیرا بر مشتری های موجود تأثیر منفی نمی گذارد. هرگونه تغییر در نوع باید در مرزهای تغییرات قابل قبول تعریف شده در اینجا برای اجرای جدید قابل قبول باشد. احتیاط شدید هنگام افزودن رابط هایی که مستقیماً بر توانایی یک طراح یا سریال ساز برای تولید کد یا داده هایی که نمی توانند در سطح پایین مصرف نشوند ، ضروری است. به عنوان مثال ، رابط iSerializable است.
❓ نیاز به قضاوت دارد: معرفی یک کلاس پایه جدید
در صورت عدم معرفی اعضای انتزاعی جدید یا تغییر معانی یا رفتار انواع موجود ، می توان یک نوع را به یک سلسله مراتب بین دو نوع موجود معرفی کرد. به عنوان مثال ، در . NET Framework 2. 0 ، کلاس DBCoection به یک کلاس پایه جدید برای SQLCoection تبدیل شد ، که قبلاً مستقیماً از مؤلفه مشتق شده بود.
✔ مجاز: انتقال یک نوع از یک مونتاژ به دیگری
مونتاژ قدیمی باید با TypeforwardEdtoattribute مشخص شود که به مونتاژ جدید اشاره دارد.
✔ مجاز: تغییر نوع ساختار به نوع ساختار readonly
تغییر نوع ساختار readonly به یک نوع ساختار مجاز نیست.
✔ مجاز: اضافه کردن کلمه کلیدی مهر و موم شده یا انتزاعی به یک نوع در صورت عدم وجود سازندگان در دسترس (عمومی یا محافظت شده)
✔ مجاز: گسترش دید یک نوع
❌ مجازات: تغییر فضای نام یا نام یک نوع
❌ مجازات: تغییر نام یا از بین بردن یک نوع عمومی
این همه کدی را که از نوع تغییر نام یا حذف شده استفاده می کند ، می شکند.
خط مشی . NET برای API های منسوخ به شرح زیر است:
API که در نسخه پشتیبانی بلند مدت (LTS) حمل می شود ، باید در نسخه بعدی LTS قبل از حذف آن منسوخ شود. در موارد نادر ، استثنائاتی برای منسوخ API قبل از انتشار LTS بعدی بر اساس نیازهای تجاری انجام می شود. همه منسوخ ها مستند و به مشتریان ابلاغ می شوند.
برای اطلاعات بیشتر در مورد خط مشی پشتیبانی . NET ، به خط مشی پشتیبانی . NET مراجعه کنید.
❌ مجازات: تغییر نوع اساسی شمارش
این یک تغییر در زمان و رفتاری کامپایل و همچنین یک تغییر شکستن باینری است که می تواند آرگومان های ویژگی را غیرقابل توصیف جلوه دهد.
❌ مجازات: آب بندی نوعی که قبلاً از آن خارج شده بود
❌ مجازات: اضافه کردن یک رابط به مجموعه انواع پایه رابط
اگر یک رابط رابط کاربری را که قبلاً پیاده سازی نکرده است ، پیاده سازی کند ، انواع مختلفی که نسخه اصلی رابط را اجرا می کنند ، شکسته می شوند.
❓ نیاز به قضاوت دارد: حذف یک کلاس از مجموعه کلاسهای پایه یا رابط از مجموعه رابط های اجرا شده
یک قاعده برای حذف رابط یک استثنا وجود دارد: می توانید اجرای یک رابط را که از رابط حذف شده مشتق شده است اضافه کنید. به عنوان مثال ، اگر نوع یا رابط کاربری IComponent را اجرا می کند ، می توانید IDISPAID را حذف کنید ، که Idisposable را پیاده سازی می کند.
❌ مجازات: تغییر یک نوع ساختار readonly به یک نوع ساختار
با این حال ، تغییر نوع ساختار به نوع ساختار readonly مجاز است.
❌ مجازات: تغییر نوع ساختار به نوع ساختار ref ، و برعکس
❌ مجازات: کاهش دید یک نوع
با این حال ، افزایش دید یک نوع مجاز است.
اعضا
✔ مجاز: گسترش دید یک عضو که مجازی نیست
✔ مجاز: اضافه کردن یک عضو انتزاعی به یک نوع عمومی که هیچ سازنده ای در دسترس (عمومی یا محافظت شده) ندارد ، یا نوع مهر و موم شده است
با این حال ، اضافه کردن یک عضو انتزاعی به نوع سازنده های قابل دسترسی (عمومی یا محافظت شده) و مهر و موم نشده است.
✔ مجاز: محدود کردن دید یک عضو حفاظت شده هنگامی که نوع سازنده در دسترس (عمومی یا محافظت شده) ندارد ، یا نوع مهر و موم شده است
✔ مجاز: انتقال یک عضو به یک کلاس بالاتر در سلسله مراتب از نوع برداشت شده از آن
✔ مجاز: اضافه کردن یا حذف یک غلبه
معرفی یک نادیده گرفتن ممکن است باعث شود مصرف کنندگان قبلی هنگام فراخوانی پایگاه ، از این امر نادیده بگیرند.
✔ مجاز: اضافه کردن یک سازنده به یک کلاس ، همراه با یک سازنده بدون پارامتر اگر کلاس قبلاً هیچ سازنده ای نداشت
با این حال ، اضافه کردن یک سازنده به کلاس که قبلاً هیچ سازنده ای بدون اضافه کردن سازنده بدون پارامتر نداشت ، مجاز نیست.
✔ مجاز: تغییر عضو از چکیده به مجازی
✔ مجاز: تغییر از یک Ref readonly به یک مقدار بازگشت REF (به جز روش های مجازی یا رابط ها)
✔ مجاز: حذف Readonly از یک زمینه ، مگر اینکه نوع استاتیک این زمینه یک نوع مقدار قابل تغییر باشد
✔ مجاز: فراخوانی یک رویداد جدید که قبلاً تعریف نشده بود
❓ نیاز به قضاوت دارد: اضافه کردن یک قسمت نمونه جدید به یک نوع
این تغییر بر سریال سازی تأثیر می گذارد.
❌ مجازات: تغییر نام یا حذف یک عضو یا پارامتر عمومی
این همه کدی را که از عضو تغییر نام یا حذف شده یا پارامتر استفاده می کند ، می شکند.
این شامل حذف یا تغییر نام یک گیرنده یا تنظیم کننده از یک ملک و همچنین تغییر نام یا حذف اعضای شمارش است.
❌ مجازات: اضافه کردن یک عضو به رابط
اگر اجرای آن را ارائه دهید ، اضافه کردن یک عضو جدید به یک رابط موجود لزوماً منجر به خرابی کامپایل در مجامع پایین دست نمی شود. با این حال ، همه زبانها از اعضای رابط پیش فرض (DIMS) پشتیبانی نمی کنند. همچنین ، در برخی از سناریوها ، زمان اجرا نمی تواند تصمیم بگیرد که کدام یک از اعضای رابط پیش فرض را فراخوانی می کند. به همین دلایل ، اضافه کردن یک عضو به یک رابط موجود ، یک تغییر شکستن محسوب می شود.
❌ مجازات: تغییر ارزش یک عضو ثابت یا عضویت عمومی
❌ مجازات: تغییر نوع خاصیت ، فیلد ، پارامتر یا مقدار بازگشت
❌ مجازات: اضافه کردن ، حذف یا تغییر ترتیب پارامترها
❌ مجاز نیست: اضافه کردن یا حذف کلمه کلیدی in ، out یا ref از یک پارامتر
❌ مجازات: تغییر نام پارامتر (از جمله تغییر پرونده آن)
این به دو دلیل در نظر گرفته می شود:
این سناریوهای دیر هنگام مانند ویژگی اتصال دیر هنگام در ویژوال بیسیک و پویا در C#را می شکند.
❌ مجازات: تغییر از مقدار بازگشت به یک مقدار بازگشت Ref Readonly
❌ مجازات: تغییر از یک Ref readonly به یک مقدار بازگشت REF در یک روش مجازی یا رابط
❌ مجازات: اضافه کردن یا حذف چکیده از یک عضو
❌ مجازات: حذف کلمه کلیدی مجازی از یک عضو
❌ مجازات: اضافه کردن کلمه کلیدی مجازی به یک عضو
در حالی که این اغلب یک تغییر شکستن نیست زیرا کامپایلر C# تمایل به انتشار دستورالعمل های زبان متوسط Callvirt (IL) برای فراخوانی روش های غیر مجازی دارد (CallVirt یک چک تهی را انجام می دهد ، در حالی که یک تماس عادی نیست) ، این رفتار غیرقابل تغییر استدلایل متعدد:
C# تنها زبانی نیست که . NET هدف قرار می دهد.
کامپایلر C# به طور فزاینده ای سعی می کند هر زمان که روش هدف غیر مجلسی باشد ، CallVirt را به یک تماس عادی بهینه کند و احتمالاً تهی نیست (مانند روشی که از طریق اپراتور انتشار NULL قابل دسترسی است).
ساخت یک روش مجازی به این معنی است که کد مصرف کننده اغلب به آن غیرقانونی می خواند.
❌ مجازات: ساخت یک عضو مجازی
یک عضو مجازی ، یک روش را ارائه می دهد که می تواند توسط یک کلاس مشتق شده بیش از حد باشد. یک عضو انتزاعی هیچ عملی را ارائه نمی دهد و باید نادیده گرفته شود.
❌ مجازات: اضافه کردن کلمه کلیدی مهر و موم شده به یک عضو رابط
افزودن مهر و موم شده به یک عضو رابط پیش فرض ، آن را غیر مجلسی می کند و از اجرای یک نوع مشتق شده از آن عضو جلوگیری می کند.
❌ مجازات: اضافه کردن یک عضو انتزاعی به یک نوع عمومی که سازندگان در دسترس (عمومی یا محافظت شده) دارند و مهر و موم نشده است
❌ مجازات: اضافه یا حذف کلمه کلیدی استاتیک از یک عضو
❌ مجاز نیست: اضافه کردن اضافه بار که مانع از اضافه بار موجود می شود و یک رفتار متفاوت را تعریف می کند
این باعث می شود مشتری های موجود که به اضافه بار قبلی محدود شده اند ، شکسته شود. به عنوان مثال ، اگر یک کلاس دارای یک نسخه واحد از روشی باشد که UINT32 را می پذیرد ، یک مصرف کننده موجود هنگام عبور از مقدار INT32 با موفقیت به آن اضافه بار متصل می شود. با این حال ، اگر یک اضافه بار اضافه کنید که یک INT32 را بپذیرد ، هنگام بازخوانی یا استفاده از اتصال دیررس ، کامپایلر اکنون به اضافه بار جدید متصل می شود. اگر رفتار متفاوت نتیجه بگیرد ، این یک تغییر شکستن است.
❌ مجازات: اضافه کردن یک سازنده به کلاس که قبلاً هیچ سازنده ای بدون اضافه کردن سازنده بدون پارامتر نداشت
❌ مجازات: اضافه کردن Readonly به یک زمینه
❌ مجازات: کاهش دید یک عضو
این شامل کاهش دید یک عضو حفاظت شده در هنگام وجود سازندگان در دسترس (عمومی یا محافظت شده) است و نوع آن مهر و موم نمی شود. اگر اینگونه نباشد ، کاهش دید یک عضو محافظت شده مجاز است.
افزایش دید یک عضو مجاز است.
❌ مجازات: تغییر نوع عضو
مقدار بازگشت یک روش یا نوع یک ویژگی یا زمینه قابل تغییر نیست. به عنوان مثال ، امضای روشی که یک شی را برمی گرداند ، نمی تواند تغییر کند تا یک رشته را برگرداند ، یا برعکس.
❌ مجازات: اضافه کردن یک زمینه به ساختاری که قبلاً هیچ وضعیتی نداشت
قوانین تعیین کننده تعیین شده اجازه می دهد تا از متغیرهای ناآگاه استفاده شود تا زمانی که نوع متغیر یک ساختار بدون تابش باشد. اگر ساختار مطرح شود ، کد می تواند با داده های ناآگاهانه به پایان برسد. این هم به طور بالقوه یک منبع شکستن منبع و تغییر شکستن باینری است.
❌ مجازات: شلیک یک رویداد موجود هنگامی که قبلاً هرگز اخراج نشد
تغییرات رفتاری
مجامع
✔ مجاز: ساخت مونتاژ قابل حمل هنگامی که هنوز همان سیستم عامل ها پشتیبانی می شوند
❌ مجازات: تغییر نام مونتاژ
❌ مجازات: تغییر کلید عمومی یک مونتاژ
خواص ، زمینه ها ، پارامترها و مقادیر بازگشت
✔ مجاز: تغییر مقدار یک ویژگی ، زمینه ، مقدار بازگشت یا پارامتر خارج به نوع مشتق شده تر
به عنوان مثال ، روشی که یک نوع شیء را برمی گرداند می تواند یک نمونه رشته را برگرداند.(با این حال ، امضای روش نمی تواند تغییر کند.)
✔ مجاز: افزایش دامنه مقادیر پذیرفته شده برای یک ویژگی یا پارامتر اگر عضو مجازی نباشد
در حالی که دامنه مقادیری که می توانند به روش منتقل شوند یا توسط عضو بازگردانده شود ، می تواند گسترش یابد ، پارامتر یا نوع عضو نمی تواند. به عنوان مثال ، در حالی که مقادیر منتقل شده به یک روش می توانند از 0-124 به 0-255 گسترش یابد ، نوع پارامتر نمی تواند از بایت به INT32 تغییر کند.
❌ مجازات: افزایش دامنه مقادیر پذیرفته شده برای یک خاصیت یا پارامتر اگر عضو مجازی باشد
این تغییر اعضای ناعادلانه موجود را خراب می کند ، که برای محدوده گسترده مقادیر به درستی کار نمی کند.
❌ مجازات: کاهش دامنه مقادیر پذیرفته شده برای یک خاصیت یا پارامتر
❌ مجازات: افزایش دامنه مقادیر برگشتی برای یک ویژگی ، زمینه ، مقدار بازگشت یا پارامتر
❌ مجازات: تغییر مقادیر برگشتی برای یک ویژگی ، زمینه ، مقدار بازگشت روش یا پارامتر خارج
❌ مجازات: تغییر مقدار پیش فرض یک ویژگی ، زمینه یا پارامتر
تغییر یا از بین بردن مقدار پیش فرض پارامتر یک استراحت باینری نیست. از بین بردن مقدار پیش فرض پارامتر یک منبع شکست است و تغییر مقدار پیش فرض پارامتر می تواند منجر به یک شکست رفتاری پس از بازپرداخت شود.
به همین دلیل ، حذف مقادیر پیش فرض پارامتر در مورد خاص "انتقال" آن مقادیر پیش فرض به یک روش جدید روش جدید برای از بین بردن ابهام قابل قبول است. به عنوان مثال ، یک روش موجود mymethod (int a = 1) را در نظر بگیرید. اگر اضافه بار MyMethod را با دو پارامتر اختیاری A و B معرفی کنید ، می توانید با انتقال مقدار پیش فرض A به اضافه بار جدید ، سازگاری را حفظ کنید. اکنون این دو بار MyMethod (int a) و mymethod (int a = 1 ، int b = 2) هستند. این الگوی به MyMethod () اجازه می دهد تا کامپایل شود.
❌ مجازات: تغییر دقت مقدار بازگشت عددی
❓ نیاز به قضاوت دارد: تغییر در تجزیه ورودی و پرتاب استثنائات جدید (حتی اگر رفتار تجزیه کننده در مستندات مشخص نشده باشد
استثناها
✔ مجاز: پرتاب یک استثناء بیشتر از یک استثناء موجود
از آنجا که استثناء جدید یک زیر کلاس از یک استثناء موجود است ، کد دست زدن به استثناء قبلی همچنان به استثناء ادامه می دهد. به عنوان مثال ، در . NET Framework 4 ، ایجاد فرهنگ و روش های بازیابی شروع به پرتاب یک CulturenotfoundException به جای یک استدلال در صورت عدم یافتن فرهنگ کرد. از آنجا که CulturenotFoundException از ArgumentException ناشی می شود ، این یک تغییر قابل قبول است.
✔ مجاز: پرتاب استثنائی که غیرقابل برگشت تلقی می شود
استثنائات غیرقابل برگشت نباید گرفتار شود بلکه در عوض باید توسط یک کنترل کننده سطح بالایی برخوردار شود. بنابراین انتظار نمی رود کاربران کدی داشته باشند که این استثنائات صریح را بدست آورد. استثنائات غیرقابل برگشت عبارتند از:
✔ مجاز: پرتاب یک استثناء جدید در یک مسیر کد جدید
این استثنا فقط باید در مورد یک کد جدید که با مقادیر پارامتر جدید یا حالت اجرا شده است اعمال شود و با کد موجود که نسخه قبلی را هدف قرار می دهد ، قابل اجرا نیست.
✔ مجاز: حذف یک استثنا برای فعال کردن رفتار قوی تر یا سناریوهای جدید
به عنوان مثال ، یک روش تقسیم که قبلاً فقط مقادیر مثبت را اداره می کرد و یک ArgrentOutofrangeException را پرتاب می کرد در غیر این صورت می تواند برای پشتیبانی از مقادیر منفی و مثبت بدون پرتاب یک استثنا ، تغییر یابد.
✔ مجاز: تغییر متن پیام خطا
توسعه دهندگان نباید به متن پیام های خطا اعتماد کنند ، که بر اساس فرهنگ کاربر نیز تغییر می کند.
❌ مجازات: پرتاب استثنا در هر مورد دیگری که در بالا ذکر نشده است
❌ مجازات: حذف یک استثنا در هر مورد دیگری که در بالا ذکر نشده است
ویژگی های
✔ مجاز: تغییر مقدار یک ویژگی که قابل مشاهده نیست
❌ مجازات: تغییر مقدار یک ویژگی که قابل مشاهده است
❓ نیاز به قضاوت دارد: حذف یک ویژگی
در بیشتر موارد ، حذف یک ویژگی (مانند nonserializedAttribute) یک تغییر شکستن است.
پشتیبانی سکو
✔ مجاز: پشتیبانی از یک عمل بر روی سکویی که قبلاً پشتیبانی نشده بود
❌ مجازات: از پشتیبانی یا نیاز به یک بسته خدمات خاص برای عملیاتی که قبلاً در یک سیستم عامل پشتیبانی می شد ، پشتیبانی نمی کند
تغییرات اجرای داخلی
❓ نیاز به قضاوت دارد: تغییر سطح سطح یک نوع داخلی
چنین تغییراتی به طور کلی مجاز است ، اگرچه بازتاب خصوصی را می شکنند. در بعضی موارد ، در جایی که کتابخانه های محبوب شخص ثالث یا تعداد زیادی از توسعه دهندگان به API داخلی بستگی دارند ، ممکن است چنین تغییراتی مجاز نباشد.
❓ نیاز به قضاوت دارد: تغییر اجرای داخلی یک عضو
این تغییرات به طور کلی مجاز است ، اگرچه بازتاب خصوصی را می شکنند. در بعضی موارد ، در مواردی که کد مشتری غالباً به بازتاب خصوصی بستگی دارد یا اینکه این تغییر عوارض جانبی ناخواسته را معرفی می کند ، ممکن است این تغییرات مجاز نباشد.
✔ مجاز: بهبود عملکرد یک عملیات
توانایی اصلاح عملکرد یک عملیات ضروری است ، اما چنین تغییراتی می تواند کدی را که به سرعت فعلی یک عملیات متکی است ، بشکند. این امر به ویژه در مورد کد که به زمان عملیات ناهمزمان بستگی دارد صادق است. تغییر عملکرد نباید تاثیری در رفتار دیگر API مورد نظر داشته باشد. در غیر این صورت ، این تغییر در حال شکستن خواهد بود.
✔ مجاز: به طور غیرمستقیم (و اغلب منفی) عملکرد یک عملیات را تغییر می دهد
اگر تغییر سؤال به دلایل دیگری به عنوان شکستن طبقه بندی نشود ، این قابل قبول است. غالباً ، اقداماتی باید انجام شود که ممکن است شامل عملیات اضافی یا عملکرد جدید باشد. این تقریباً همیشه بر عملکرد تأثیر می گذارد اما ممکن است برای عملکرد API مورد نظر همانطور که انتظار می رود ضروری باشد.
❌ مجازات: تغییر یک API همزمان به ناهمزمان (و برعکس)
تغییر کد
✔ مجاز: اضافه کردن پارامترها به یک پارامتر
❌ مجازات: تغییر ساختار به یک کلاس و برعکس
❌ مجازات: اضافه کردن کلمه کلیدی بررسی شده به یک بلوک کد
این تغییر ممکن است باعث کدی شود که قبلاً برای پرتاب یک OverflowException اجرا شده و غیرقابل قبول است.
❌ مجازات: حذف پارامترها از یک پارامتر
❌ مجازات: تغییر ترتیب وقوع وقایع
توسعه دهندگان می توانند به طور منطقی انتظار داشته باشند که رویدادها به همان ترتیب آتش بگیرند و کد توسعه دهنده غالباً به ترتیب اخراج حوادث بستگی دارد.
❌ مجازات: حذف یک رویداد در یک عمل معین
❌ مجازات: تغییر تعداد وقایع مشخص شده نامیده می شود
❌ مجازات: اضافه کردن پرچم گذاری به نوع شمارش
پایگاه های معاملاتی...
ما را در سایت پایگاه های معاملاتی دنبال می کنید
برچسب :
نویسنده : فرشته صدرعرفایی
بازدید : <-PostHit->
تاريخ : پنجشنبه
3 فروردين
1402 ساعت: 12:14