اشتراکها
Summary
JavaScript is a language written for websites to run in the client’s browser.
AJAX is a way for JavaScript to request data from a server without refreshing the page or blocking the application.
jQuery is a JavaScript library built to automate and simplify common web tasks like AJAX or animation.
Angular is a hip JavaScript framework which is made for building large, single-page web applications.
Node.js allows JavaScript to be run without a browser, and is commonly used to run web servers.
اضافه شدن Generic Parsing به دات نت 7
تا قبل از دات نت 7، متدهای Parse و TryParse جزو استاندارد اغلب نوعها در دات نت بودند؛ اما امکان استفادهی جنریک از آنها وجود نداشت. این مشکل به لطف وجود اعضای استاتیک اینترفیسها در دات نت 7 و C# 11 برطرف شدهاست. برای این منظور دو اینترفیس جدید System.IParsable و System.ISpanParsable به دات نت 7 اضافه شدهاند که امکان دسترسی به متد T.Parse را میسر میکنند.
دو نمونه مثال از نحوهی استفادهی از این API جدید را در ادامه مشاهده میکنید:
اگر میخواستیم متد ParseIt را به صورت جنریک و بدون استفاده از ویژگیهای جدید زبان #C و دسترسی مستقیم به T.Parse بنویسیم، یک روش آن، استفاده از Reflection به صورت زیر میبود:
تا قبل از دات نت 7، متدهای Parse و TryParse جزو استاندارد اغلب نوعها در دات نت بودند؛ اما امکان استفادهی جنریک از آنها وجود نداشت. این مشکل به لطف وجود اعضای استاتیک اینترفیسها در دات نت 7 و C# 11 برطرف شدهاست. برای این منظور دو اینترفیس جدید System.IParsable و System.ISpanParsable به دات نت 7 اضافه شدهاند که امکان دسترسی به متد T.Parse را میسر میکنند.
دو نمونه مثال از نحوهی استفادهی از این API جدید را در ادامه مشاهده میکنید:
public static T ParseIt<T>(string content, IFormatProvider? provider) where T : IParsable<T> { return T.Parse(content, provider); } public IEnumerable<T> ParseCsvRow<T>(string content, IFormatProvider? provider) where T : IParsable<T> { return content.Split(',').Select(str => T.Parse(str, provider)); }
public static T ParseIt<T>(string content, IFormatProvider? provider) { var type = typeof(T); var method = type.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, new[] { typeof(string), typeof(IFormatProvider) }); return (T)method!.Invoke(null, new object?[] { content, provider })!; }
تا پیش از C# 6 یکی از روشهای توصیه شدهی جهت اتصال رشتهها به هم، استفاده از متدهایی مانند string.Format و StringBuilder.AppendFormat بود:
مشکل این روش، کاهش خوانایی آن با بالا رفتن تعداد پارامترهای متد Format است و همچنین گاهی از اوقات فراموش کردن مقدار دهی بعضی از آنها و یا حتی ذکر ایندکسهایی غیر معتبر که در زمان اجرا، برنامه را با یک خطا متوقف میکنند.
در C# 6 جهت رفع این مشکلات، راه حلی به نام String interpolation ارائه شدهاست و اگر افزونهی ReSharper یا یکی از افزونههای Roslyn را نصب کرده باشید، به سادگی امکان تبدیل کدهای قدیمی را به فرمت جدید آن خواهید یافت:
در این حالت کد قدیمی فوق، به کد ذیل تبدیل خواهد شد:
در اینجا ابتدا یک $ در ابتدای رشته قرار گرفته و سپس هر متغیر به داخل {} انتقال یافتهاست. همچنین دیگر نیازی هم به ذکر string.Format نیست.
عملیاتی که در اینجا توسط کامپایلر صورت خواهد گرفت، تبدیل این کدهای جدید مبتنی بر String interpolation به همان string.Format قدیمی در پشت صحنهاست. بنابراین این قابلیت جدید C# 6 را به کدهای قدیمی خود نیز میتوانید اعمال کنید. فقط کافی است VS 2015 را نصب کرده باشید و دیگر شمارهی دات نت فریم ورک مورد استفاده مهم نیست.
امکان انجام محاسبات با String interpolation
زمانیکه $ در ابتدای رشته قرار گرفت، عبارات داخل {}ها توسط کامپایلر محاسبه و جایگزین میشوند. بنابراین میتوان چنین محاسباتی را نیز انجام داد:
بدیهی اگر $ ابتدای رشته فراموش شود، اتفاق خاصی رخ نخواهد داد.
تغییر فرمت عبارات نمایش داده شده توسط String interpolation
همانطور که با string.Format میتوان نمایش سه رقم جدا کنندهی هزارها را فعال کرد و یا تاریخی را به نحوی خاص نمایش داد، در اینجا نیز همان قابلیتها برقرار هستند و باید پس از ذکر یک : عنوان شوند:
حالت کلی و استاندارد آن در متد string.Format به صورت {index[,alignment][:formatString]} است.
سفارشی سازی String interpolation
اگر متغیر رشتهای معرفی شدهی توسط $ را با یک var مشخص کنیم، نوع آن به صورت پیش فرض، از نوع string خواهد بود. برای نمونه در مثالهای فوق، message و message2 از نوع string تعریف میشوند. اما این رشتههای ویژه را میتوان از نوع IFormattable و یا FormattableString نیز تعریف کرد.
در حقیقت رشتههای آغاز شدهی با $ از نوع IFormattable هستند و اگر نوع متغیر آنها ذکر نشود، به صورت خودکار به نوع FormattableString که اینترفیس IFormattable را پیاده سازی میکند، تبدیل میشوند. بنابراین پیاده سازی این اینترفیس، امکان سفارشی سازی خروجی string interpolation را میسر میکند. برای نمونه میخواهیم در مثال message2، نحوهی نمایش تاریخ را سفارشی سازی کنیم.
در اینجا ابتدا کار با پیاده سازی اینترفیس IFormatProvider شروع میشود. متد GetFormat آن همیشه به همین شکل خواهد بود و هر زمانیکه نوع ارسالی به آن ICustomFormatter بود، یعنی یکی از اجزای {} دار در حال آنالیز است و خروجی مدنظر آن همیشه از نوع ICustomFormatter است که نمونهای از پیاده سازی آنرا جهت سفارشی سازی DateTime ملاحظه میکنید.
پس از پیاده سازی این سفارشی کنندهی تاریخ، نحوهی استفادهی از آن به صورت ذیل است:
ابتدا یک متد static را تعریف کنید که ورودی آن از نوع FormattableString باشد؛ از این جهت که رشتههای شروع شدهی با $ نیز از همین نوع هستند. سپس سفارشی سازی پردازش {}ها در قسمت ToString آن انجام میشود و در اینجا میتوان یک IFormatProvider جدید را معرفی کرد.
در ادامه برای اعمال این سفارشی سازی، فقط کافی است متد formatMyDate را به رشتهی مدنظر اعمال کنیم:
و اگر تنها میخواهید فرهنگ جاری را عوض کنید، از روش سادهی زیر استفاده نمائید:
در اینجا با اعمال متد faIr به عبارت شروع شدهی با $، فرهنگ ایران به رشتهی جاری اعمال خواهد شد.
نمونهی کاربردیتر آن اعمال InvariantCulture به String interpolation است:
یک نکته: همانطور که عنوان شد این قابلیت جدید با نگارشهای قبلی دات نت نیز سازگار است؛ اما این کلاسهای جدید را در این نگارشها نخواهید یافت. برای رفع این مشکل تنها کافی است این کلاسهای یاد شده را به صورت دستی در فضای نام اصلی آنها تعریف و اضافه کنید. یک مثال
غیرفعال سازی String interpolation
اگر میخواهید در رشتهای که با $ شروع شده، بجای محاسبهی عبارتی، دقیقا خود آنرا نمایش دهید (و { را escape کنید)، از {{}} استفاده کنید:
در این مثال اولین {} محاسبه خواهد شد و دومی خیر.
پردازش عبارات شرطی توسط String interpolation
همانطور که عنوان شد، امکان ذکر یک عبارت کامل هم در بین {} وجود دارد (محاسبات، ذکر یک عبارت LINQ، ذکر یک متد و امثال آن). اما در این میان اگر یک عبارت شرطی مدنظر بود، باید بین () قرار گیرد:
علت اینجا است که کامپایلر سیشارپ، : بین {} را به format specifier تفسیر میکند. نمونهی آنرا پیشتر با مثال «تغییر فرمت عبارات نمایش داده شده» ملاحظه کردید. ذکر : در اینجا به معنای شروع مشخص سازی فرمتی است که قرار است به این حاصل اعمال شود. برای تغییر این رفتار پیش فرض، کافی است عبارت مدنظر را بین () ذکر کنیم تا تمام آن به صورت یک عبارت سیشارپ تفسیر شود.
using System; namespace CS6NewFeatures { class Person { public string FirstName { set; get; } public string LastName { set; get; } public int Age { set; get; } } class Program { static void Main(string[] args) { var person = new Person { FirstName = "User 1", LastName = "Last Name 1", Age = 50 }; var message = string.Format("Hello! My name is {0} {1} and I am {2} years old.", person.FirstName, person.LastName, person.Age); Console.Write(message); } } }
در C# 6 جهت رفع این مشکلات، راه حلی به نام String interpolation ارائه شدهاست و اگر افزونهی ReSharper یا یکی از افزونههای Roslyn را نصب کرده باشید، به سادگی امکان تبدیل کدهای قدیمی را به فرمت جدید آن خواهید یافت:
در این حالت کد قدیمی فوق، به کد ذیل تبدیل خواهد شد:
static void Main(string[] args) { var person = new Person { FirstName = "User 1", LastName = "Last Name 1", Age = 50 }; var message = $"Hello! My name is {person.FirstName} {person.LastName} and I am {person.Age} years old."; Console.Write(message); }
عملیاتی که در اینجا توسط کامپایلر صورت خواهد گرفت، تبدیل این کدهای جدید مبتنی بر String interpolation به همان string.Format قدیمی در پشت صحنهاست. بنابراین این قابلیت جدید C# 6 را به کدهای قدیمی خود نیز میتوانید اعمال کنید. فقط کافی است VS 2015 را نصب کرده باشید و دیگر شمارهی دات نت فریم ورک مورد استفاده مهم نیست.
امکان انجام محاسبات با String interpolation
زمانیکه $ در ابتدای رشته قرار گرفت، عبارات داخل {}ها توسط کامپایلر محاسبه و جایگزین میشوند. بنابراین میتوان چنین محاسباتی را نیز انجام داد:
var message2 = $"{Environment.NewLine}Test {DateTime.Now}, {3*2}"; Console.Write(message2);
تغییر فرمت عبارات نمایش داده شده توسط String interpolation
همانطور که با string.Format میتوان نمایش سه رقم جدا کنندهی هزارها را فعال کرد و یا تاریخی را به نحوی خاص نمایش داد، در اینجا نیز همان قابلیتها برقرار هستند و باید پس از ذکر یک : عنوان شوند:
var message3 = $"{Environment.NewLine}{1000000:n0} {DateTime.Now:dd-MM-yyyy}"; Console.Write(message3);
سفارشی سازی String interpolation
اگر متغیر رشتهای معرفی شدهی توسط $ را با یک var مشخص کنیم، نوع آن به صورت پیش فرض، از نوع string خواهد بود. برای نمونه در مثالهای فوق، message و message2 از نوع string تعریف میشوند. اما این رشتههای ویژه را میتوان از نوع IFormattable و یا FormattableString نیز تعریف کرد.
در حقیقت رشتههای آغاز شدهی با $ از نوع IFormattable هستند و اگر نوع متغیر آنها ذکر نشود، به صورت خودکار به نوع FormattableString که اینترفیس IFormattable را پیاده سازی میکند، تبدیل میشوند. بنابراین پیاده سازی این اینترفیس، امکان سفارشی سازی خروجی string interpolation را میسر میکند. برای نمونه میخواهیم در مثال message2، نحوهی نمایش تاریخ را سفارشی سازی کنیم.
class MyDateFormatProvider : IFormatProvider { readonly MyDateFormatter _formatter = new MyDateFormatter(); public object GetFormat(Type formatType) { return formatType == typeof(ICustomFormatter) ? _formatter : null; } class MyDateFormatter : ICustomFormatter { public string Format(string format, object arg, IFormatProvider formatProvider) { if (arg is DateTime) return ((DateTime)arg).ToString("MM/dd/yyyy"); return arg.ToString(); } } }
پس از پیاده سازی این سفارشی کنندهی تاریخ، نحوهی استفادهی از آن به صورت ذیل است:
static string formatMyDate(FormattableString formattable) { return formattable.ToString(new MyDateFormatProvider()); }
در ادامه برای اعمال این سفارشی سازی، فقط کافی است متد formatMyDate را به رشتهی مدنظر اعمال کنیم:
var message2 = formatMyDate($"{Environment.NewLine}Test {DateTime.Now}, {3*2}"); Console.Write(message2);
و اگر تنها میخواهید فرهنگ جاری را عوض کنید، از روش سادهی زیر استفاده نمائید:
public static string faIr(IFormattable formattable) { return formattable.ToString(null, new CultureInfo("fa-Ir")); }
نمونهی کاربردیتر آن اعمال InvariantCulture به String interpolation است:
static string invariant(FormattableString formattable) { return formattable.ToString(CultureInfo.InvariantCulture); }
یک نکته: همانطور که عنوان شد این قابلیت جدید با نگارشهای قبلی دات نت نیز سازگار است؛ اما این کلاسهای جدید را در این نگارشها نخواهید یافت. برای رفع این مشکل تنها کافی است این کلاسهای یاد شده را به صورت دستی در فضای نام اصلی آنها تعریف و اضافه کنید. یک مثال
غیرفعال سازی String interpolation
اگر میخواهید در رشتهای که با $ شروع شده، بجای محاسبهی عبارتی، دقیقا خود آنرا نمایش دهید (و { را escape کنید)، از {{}} استفاده کنید:
var message0 = $"Hello! My name is {person.FirstName} {{person.FirstName}}";
پردازش عبارات شرطی توسط String interpolation
همانطور که عنوان شد، امکان ذکر یک عبارت کامل هم در بین {} وجود دارد (محاسبات، ذکر یک عبارت LINQ، ذکر یک متد و امثال آن). اما در این میان اگر یک عبارت شرطی مدنظر بود، باید بین () قرار گیرد:
Console.Write($"{(person.Age>50 ? "old": "young")}");
نظرات مطالب
C# 6 - String Interpolation
یک نکتهی تکمیلی: روش رفع خطای CA1305
با «غنی سازی کامپایلر C# 9.0 با افزونهها» نکات جالبی در کدها ظاهر میشوند که یکی از آنها CA1305 است و به صورت خلاصه عنوان میکند که اگر متد در حال استفاده، دارای overload ای با پارامتر از نوع IFormatProvider هست، حتما از آن استفاده کنید؛ تا شاهد مشکلاتی مانند « تغییرات مهم مقایسهی رشتهها در NET 5.0. » و « از متد DateTime.ToString بدون پارامتر استفاده نکنید! » نباشید. در مورد C# 6 - String Interpolation در متن جاری نکاتی در این مورد عنوان شدهاست. میتوان جهت تکمیل آن، نکتهی ریز زیر را هم مدنظر داشت:
using static System.FormattableString; var number = 11; var text = Invariant($"{number}");
صورت مساله که مشخصه قراره دیتای رو از منبع دادهی Xml به model مورد نظرمون نگاشت کنیم چیزی شبیه کاری که متد GetEntries انجام میده و تو این پست معرفی شده...
AutoMapper به صورت داخلی و با استفاده از قراردادها نمیتونه xml رو به object تبدیل کنه ولی این کار به کمک LINQ to XML قابل انجامه.
مثالی که برای این پست انتخاب شده سوژهی داغ روزهای اخیره ؟!
مدل زیر رو در نظر داشته باشید
قراره از یک وب سرویس اطلاعات مربوط به فلزات گرانبها رو دریافت و به مدل PreciousMetal نگاشت کنیم.ساختار اطلاعات دریافتی ما به شکل زیره
برای نگاشتهای معمولی کار سختی نداریم و از MapFrom استفاده میکنیم مثلا برای قیمت
ولی برای زمان دریافت قیمت با توجه به متفاوت بودن زمان دریافتی مثلا در اینجا Unix time از Custom value resolvers استفاده میکنیم
همچنیا میخواهیم از معادل فارسی نام فلزات گرانبها استفاده کنیم
نکته:از سری قبلی آشنایی با AutoMapper همیشه بین انتخاب Custom Value Formatters و Custom value resolvers مشکل داشتم مثلا همین قسمت بنظر خودم Custom Value Formatters مناسبتر میاد بعد کمی وقت گذاشتن مشخص شد گویا یه جورایی Custom Value Formatters اضافه س و اشتباه تو طراحی بوده.
و اما نحوه استفاده
AutoMapper به صورت داخلی و با استفاده از قراردادها نمیتونه xml رو به object تبدیل کنه ولی این کار به کمک LINQ to XML قابل انجامه.
مثالی که برای این پست انتخاب شده سوژهی داغ روزهای اخیره ؟!
مدل زیر رو در نظر داشته باشید
public class PreciousMetal { public string Name { get; set; } public float Price { get; set; } public DateTime UpdateTime { get; set; } }
قراره از یک وب سرویس اطلاعات مربوط به فلزات گرانبها رو دریافت و به مدل PreciousMetal نگاشت کنیم.ساختار اطلاعات دریافتی ما به شکل زیره
<pricelist currency="usd"> <price timestamp="1349347920" per="ozt" commodity="gold">1788.70</price> <price timestamp="1349347860" per="ozt" commodity="palladium">665.50</price> <price timestamp="1349347920" per="ozt" commodity="platinum">1701.25</price> <price timestamp="1349347920" per="ozt" commodity="silver">34.91</price> </pricelist>
برای نگاشتهای معمولی کار سختی نداریم و از MapFrom استفاده میکنیم مثلا برای قیمت
Mapper.CreateMap<XElement, PreciousMetal>().ForMember(des => des.Price, op => op.MapFrom(src => src.Value));
ولی برای زمان دریافت قیمت با توجه به متفاوت بودن زمان دریافتی مثلا در اینجا Unix time از Custom value resolvers استفاده میکنیم
public class UnixTimestampResolver : ValueResolver<XElement, DateTime> { protected override DateTime ResolveCore(XElement source) { var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0); return origin.AddSeconds(Convert.ToDouble(source.Attribute("timestamp").Value)); } }
همچنیا میخواهیم از معادل فارسی نام فلزات گرانبها استفاده کنیم
public class EnglishPMetalToFarsiResolver : ValueResolver<XElement, string> { readonly Dictionary<string, string> _pMetaldic = new Dictionary<string, string> { {"gold", "طلا"}, {"palladium", "پالادیوم"}, {"platinum", "پلاتین "}, {"silver", "نقره"} }; protected override string ResolveCore(XElement source) { string pMetalFarsi; return _pMetaldic.TryGetValue(source.Attribute("commodity").Value, out pMetalFarsi) ? pMetalFarsi : string.Empty; } }
نکته:از سری قبلی آشنایی با AutoMapper همیشه بین انتخاب Custom Value Formatters و Custom value resolvers مشکل داشتم مثلا همین قسمت بنظر خودم Custom Value Formatters مناسبتر میاد بعد کمی وقت گذاشتن مشخص شد گویا یه جورایی Custom Value Formatters اضافه س و اشتباه تو طراحی بوده.
و اما نحوه استفاده
static void Main(string[] args) { //تعریف نگاشتها Mapper.CreateMap<XElement, PreciousMetal>().ForMember(des => des.Name, op => op.ResolveUsing<EnglishPMetalToFarsiResolver>()) .ForMember(des => des.Price, op => op.MapFrom(src => src.Value)) .ForMember(des => des.UpdateTime, op => op.ResolveUsing<UnixTimestampResolver>()); Mapper.AssertConfigurationIsValid(); //دریافت قیمتها از منبع داده var doc = XDocument.Load("http://www.xmlcharts.com/cache/precious-metals.xml"); var priceData = doc.Descendants("pricelist").Take(1).Elements("price"); //فراخوانی نگاشت var preciousMetals = Mapper.Map<IEnumerable<XElement>, IList<PreciousMetal>>(priceData); foreach (var preciousMetal in preciousMetals) { Console.WriteLine(preciousMetal.Name + " " + preciousMetal.Price + " " + preciousMetal.UpdateTime.ToShortDateString()); } Console.ReadLine(); }
DateTime در طبقه بندی سی شارپ، جزء Strcut Typeها قرار میگیرد . عمدتا از DateTime برای مدیریت تاریخ، زمان و یا تاریخ-زمان استفاده میشود. خیلی از اوقات ما نیاز داریم تا رشتهای را به نوع تاریخ تبدیل کنیم تا بتوانیم عملیات مختلفی، همچون محاسبهی اختلاف دو تاریخ، روز هفته، روز ماه و غیره را بدست آوریم. در دات نت متدهای مختلفی وجود دارند که جداسازی تاریخ را از یک رشته برای ما فراهم میکنند:
تابع () DateTime.Parse :
تابع ()DateTime.ParsExact
تابع ()DateTime.TryParse
تابع ()DateTime.TryParseExact
آنالیز متدهای معرفی شده
- Convert.ToDateTime()
- DateTime.Parse()
- DateTime.ParseExact()
- DateTime.TryParse()
- DateTime.TryParseExact()
در این مطلب این متدها و تفاوت آنها را بررسی میکنیم.
تابع ()Convert.ToDateTime
این تابع یک رشتهی با فرمت مشخص را به تاریخ و زمان تبدیل میکند. overloadها مختلف این تابع را در بخش زیر مشاهده میکنید:
• ToDateTime(string value)
Value : رشتهای از تاریخ و زمان است.
• ToDateTime(string value,IFormatProvider provide)
Value : رشتهای از تاریخ و زمان است.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
CultureInfo culture = new CultureInfo("en-US"); DateTime tempDate = Convert.ToDateTime("1/1/2010 12:10:15 PM", culture);
در اینجا en-us اطلاعاتی را دربارهی فرهنگ کشور آمریکا، ارائه میدهد. لیست کامل فرهنگهای موجود در net. را میتوانید در اینجا مشاهده کنید.
اگر رشتهی ما تهی (null) نباشد، بصورت درونی متد ()DateTime.Parse فراخوانی و نتیجهی آن بازگردانده میشود. اما در صورتیکه رشتهی ارسالی ما تهی باشد، مقدار بازگردانده شده مقدار DateTime.MinValue که برابر با 0001/1/1 میباشد، بازگردانده میشود.
نمونهای از خروجی این تابع با ورودیهای مختلف :
string datestr = null; Console.WriteLine(Convert.ToDateTime(datestr));//0001-01-01T00:00:00 datestr = "wrong string"; Console.WriteLine(Convert.ToDateTime(datestr)); //Unhandled Exception: System.FormatException: //The string was not recognized as a valid DateTime. //There is an unknown word starting at index 0. datestr = "Tue Dec 30,2015"; Console.WriteLine(Convert.ToDateTime(datestr)); //Unhandled Exception: System.FormatException: //String was not recognized as a valid DateTime.
تابع () DateTime.Parse :
این تابع یک رشتهی با فرمت مشخص را به تاریخ و زمان تبدیل میکند. دو overload این تابع را در زیر میبینیم:
• DateTime.Parse(string value)
Value : رشتهای از تاریخ و زمان میباشد.
• DateTime.Parse(String value, IFormatProvider provider)
Value : رشتهای از تاریخ و زمان میباشد.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
- DateTime.Parse(String value, IFormatProvider provider, DateTypeStyles styles)
Value : رشتهای از تاریخ و زمان میباشد.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند
Styles : از طریق این پارامتر، تنظیمات قالب بندی رشتهی دریافتی برای سفارشی سازی کردن عملیات پردازش تعریف میشود. فرض کنید میخواهید کلیهی فضاهای خالی (Space) را که قبل و بعد از رشتهی تاریخ هستند و نه در داخل رشتهی تاریخ، در زمان جداسازی و پردازش در نظر نگیرید. این پارامتر میتواند در اینجا به کمک ما بیاید (DateTimeStyles.AllowWhiteSpaces). مشاهدهی لیست کامل این خصوصیت از اینجا.
اگر مقدار رشته تهی باشد، استثنای Null نمایش داده خواهد شد (ArgumentNullException ) و اگر فرمت تاریخ ورودی صحیح نباشد، با استثنای فرمت غیرمعتبر روبرو خواهیم شد (FormatException).
string datestr = null; Console.WriteLine(DateTime.Parse(datestr)); //// Exception: Argument null exception datestr = "wrong string"; Console.WriteLine(DateTime.Parse(datestr)); //// Exception: The string was not recognized as a valid DateTime. //// There is an unknown word starting at index 0. datestr = "Tue Dec 30, 2015"; //Unhandled Exception: System.FormatException: //String was not recognized as a valid DateTime. Console.WriteLine(DateTime.Parse(datestr));
تابع ()DateTime.ParsExact
این تابع رشتهای شامل تاریخ و زمان را بههمراه فرهنگ ارسالی، دریافت و آن را تبدیل به نوع DateTime میکند. فرمت رشتهی ارسالی باید با فرمت استاندارد رشتهی تاریخ یکسان باشد. overloadهای مختلف این تابع را در زیر مشاهده میکنید:
• DateTime.ParseExact(string value, string format, IFormatProvider provider)
Value : رشتهای از تاریخ و زمان میباشد.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
• DateTime.ParseExact(string value, string format, IFormatProvider provider, DateTimeStyles style)
Value : رشتهای از تاریخ و زمان میباشد.
Format : فرمت مورد نظر برای نمایش تاریخ بعد از تبدیل را مشخص میکند.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
Styles : از طریق این پارامتر تنظیمات قالب بندی رشته برای سفارشی کردن عملیات جداسازی و پردازش تعریف میشود.
• DateTime.ParseExact(string value, string[] formats, IFormatProvider provider, DateTimeStyles style)
Value : رشتهای از تاریخ و زمان میباشد.
Format : فرمت مورد نظر برای نمایش تاریخ بعد از تبدیل را مشخص میکند. تفاوت این حالت با حالت قبل این است که لیستی از فرمتها را قبول میکند و حداقل باید یک فرمت با رشتهی ارسالی قابل انطباق باشد.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
Styles : از طریق این پارامتر تنظیمات قالب بندی رشته برای سفارشی کردن عملیات جداسازی تعریف میشود.
اگر مقدار رشته تهی باشد، استثنای Null نمایش داده خواهد شد (ArgumentNullException) و اگر فرمت تاریخ ورودی صحیح نباشد، با استثنای فرمت غیرمعتبر روبرو خواهیم شد(FormatException).
فرمت رشتهی تاریخ حتما باید با فرمت نوع تاریخ منطبق باشد. به همین منظور حالتهای مختلفی را در آرایه میتوان پیش بینی کرد. بطور مثال ممکن است رشتهی ما بصورت "2012-2-1" و یا "2012/2/1" باشد. بنابر این فرمتهای "MM/dd/yyyy" و "MM-dd-yyyy" را از طریق آرایه ارسال میکنیم.
برای درک بهتر این موضوع، کدهای زیر را مشاهده کنید:
string datestr = null; CultureInfo provider = CultureInfo.InvariantCulture; Console.WriteLine(DateTime.ParseExact(datestr, "mm/dd/yyyy", provider)); //Unhandled Exception: System.ArgumentNullException: //String reference not set to an instance of a String. datestr = "wrong date"; Console.WriteLine(DateTime.ParseExact(datestr, "mm/dd/yyyy", provider)); //Unhandled Exception: System.FormatException: //String was not recognized as a valid DateTime datestr = "Tue Dec 30, 2015"; Console.WriteLine(DateTime.ParseExact(datestr, "mm/dd/yyyy", provider)); //Unhandled Exception: System.FormatException: //String was not recognized as a valid DateTime. datestr = "10-22-2015"; Console.WriteLine(DateTime.ParseExact(datestr, "MM-dd-yyyy", provider)); //30/07/1394 12:00:00 ق.ظ datestr = "10-22-2015"; Console.WriteLine(DateTime.ParseExact(datestr, new string[] {"MM-dd-yyyy", "MM/dd/yyyy", "MM.dd.yyyy"}, provider, DateTimeStyles.None)); //30/07/1394 12:00:00 ق.ظ
تابع ()DateTime.TryParse
این تابع رشتهای شامل تاریخ و زمان را بههمراه فرهنگ مشخصی، دریافت و آن را تبدیل به نوع DateTime میکند و یک مقدار bool را برای اعلان این موضوع که عملیات تبدیل با موفقیت انجام شده است یا خیر، باز میگرداند. فرمت رشتهی ارسالی باید با فرمت رشتهی تاریخ یکسان باشد. overloadهای مختلف این تابع را در زیر مشاهده میکنید:
• DateTime.TryParse (String value, out DateTime result)
Value : رشتهای از تاریخ و زمان میباشد.
Result : مقدار تاریخ را بعد از جداسازی در خود ذخیره میکند.
• DateTime.TryParse(String value, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
Value : رشتهای از تاریخ و زمان میباشد.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
Styles : از طریق این پارامتر میتوانیم قالب پارامتر ارسالی را مشخص کنیم (مثلا نادیده گرفتن فضاهای خالی).
Result : مقدار تاریخ را بعد از جداسازی در خود ذخیره میکند.
این تابع همیشه سعی میکند رشتهی تاریخ را جداسازی کند. در صورت موفقیت در عملیات جداسازی رشته، تاریخ معتبر را بر میگرداند و در غیر اینصورت میزان کوچکترین تاریخ یا همان MinValue را که قبلا در همین مطلب اشاره شد، باز میگرداند. در صورتی هم که رشته، null یا با فرمت رشتهای غیر معتبر باشد، همان مقدار DateTime.MinValue بازگردانده میشود. همیشه با کنترل مقدار بازگشتی صحت انجام عملیات را میتوان متوجه شد (true موفقیت در عملیات جداسازی و false عدم موفقیت در عملیات جداسازی).
نکتهی مهم عدم پرتاب استثتاء در صورت عدم موفقیت در عملیات جداسازی میباشد.
string datestr = null; DateTime temp; Console.WriteLine(DateTime.TryParse(datestr, out temp)); Console.WriteLine(temp); //False //0001 - 01 - 01T00: 00:00 datestr = "wrong date"; Console.WriteLine(DateTime.TryParse(datestr, out temp)); Console.WriteLine(temp); //False //0001 - 01 - 01T00: 00:00 datestr = "Tue Dec 30, 2015"; Console.WriteLine(DateTime.TryParse(datestr, out temp)); Console.WriteLine(temp); //False //0001 - 01 - 01T00: 00:00
تابع ()DateTime.TryParseExact
این تابع رشتهای شامل تاریخ و زمان را بههمراه فرهنگ مشخصی دریافت و آن را تبدیل به نوع DateTime میکند. رشتهی ارسالی باید منطبق با فرمت تاریخ باشد. overloadهای مختلف این تابع را در زیر مشاهده میکنید:
• DateTime.ParseExact(string value, string format, IFormatProvider provider, DateTimeStyles style)
Value : رشتهای از تاریخ و زمان میباشد.
Format : فرمت مورد نظر را برای نمایش تاریخ بعد از تبدیل، مشخص میکند.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
Styles : از طریق این پارامتر میتوانیم قالب پارامتر ارسالی را مشخص کنیم (مثلا نادیده گرفتن فضاهای خالی).
• DateTime.ParseExact(string value, string[] formats, IFormatProvider provider, DateTimeStyles style)
Value : رشتهای از تاریخ و زمان میباشد.
Format : فرمت مورد نظر برای نمایش تاریخ بعد از تبدیل را مشخص میکند و میتوان آرایهای از فرمتها را در اینجا ارسال کرد.
Provider : اطلاعات فرهنگ مورد نظر را فراهم میکند.
Styles : از طریق این پارامتر میتوانیم قالب پارامتر ارسالی را مشخص کنیم (مثلا نادیده گرفتن فضاهای خالی).
این تابع را در شرایط زیر، مقدار (MinValue( 1/1/0001 12:00:00 AM را باز میگرداند:
• رشته null باشد.
• رشته خالی باشد "".
• فرمت رشتهی تاریخ غلط باشد.
• رشته با فرمت معرفی شدهی در provider، انطباق نداشته باشد.
• تنها در صورتی که مقدار انتخابی DateTimeStyle معتبر نباشد، استثنایی رخ میدهد.
همچنین همیشه پس از انجام عملیات، مقداری بولین را برای نمایش موفقیت یا عدم موفقیت جدا سازی، باز میگرداند.
string datestr = null; DateTime temp; CultureInfo provider = CultureInfo.InvariantCulture; Console.WriteLine(DateTime.TryParseExact(datestr, "MM/dd/yyyy", provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp); //False //1/1/0001 12:00:00 AM datestr = "wrong date"; Console.WriteLine(DateTime.TryParseExact(datestr, "MM/dd/yyyy", provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp); //False //1/1/0001 12:00:00 AM datestr = "Tue Dec 30, 2015"; Console.WriteLine(DateTime.TryParseExact(datestr, "MM/dd/yyyy", provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp); //False //1/1/0001 12:00:00 AM datestr = "10‐22‐2015"; Console.WriteLine(DateTime.TryParseExact(datestr, "MM/dd/yyyy", provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp); //False //1/1/0001 12:00:00 AM datestr = "10‐22‐2015"; Console.WriteLine(DateTime.TryParseExact( datestr, "MM‐dd‐yyyy", provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp); //True //30/07/1394 12:00:00 ق.ظ datestr = "10‐12‐2015"; Console.WriteLine(DateTime.TryParseExact(datestr, new string[] { "MM/dd/yyyy", "MM‐dd‐yyyy", "MM.dd.yyyy" }, provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp); //True //20/07/1394 12:00:00 ق.ظ
آنالیز متدهای معرفی شده
نوع DateTime متدهای مختلفی را برای جداسازی رشته و تبدیل آن به تاریخ دارد. تفاوتهای این متدها را در بخش زیر بررسی میکنیم :
تفاوت Parse و ConvertToDateTime:
این دو متد شبیه به هم هستند، اما چند تفاوت کوچک با هم دارند:
• اگر مقدار رشتهی ارسالی null باشد، متد Parse، یک استثناء را ارسال میکند و متد ConvertToDateTime مقدار MinValue را باز میگرداند.
• در متد Parse میتوان یک پارامتر اضافهتر نیز ارسال کرد که DateTimeStyle نامیده میشود. اما متد ConvertToDateTime این قابلیت را ندارد.
• تابع ConvertToDateTime بهصورت داخلی از متد DateTime.Parse بههمراه فرهنگ جاری استفاده میکند.
تفاوت تابع Parseو ParseExact:
این دو تابع شبیه به هم هستند، اما میتوان یک پارامتر اضافی format را نیز به تابع ParseExact ارسال کرد. این پارامتر به ما کمک میکند تا رشتهای را که فرمت متفاوتی از حالت معمولی دارد، به تاریخ تبدیل کنیم. مثلا اگر رشتهی "11232015" بدین شکل باشد، فرمت باید به شکل "MMddyyyy" تعریف شود.
تفاوت تابع Parse و TryParse:
این دو متد شبیه به هم هستند، با این تفاوت که تابع TryParse در صورت عدم موفقیت جداسازی رشته، استثنائی را ارسال نمیکند و همیشه مقدار MinValue را در صورت عدم موفقیت در تبدیل، باز میگرداند.
تفاوت تابع TryParse و TryParseExact:
هر دو تابع شبیه به هم هستند؛ بجز در پارامتر format. تابع TryParseExact از یک پارامتر اضافهی format استفاده میکند. ولی تابع TryParse اگر نتواند فرمت مورد نیاز را فراهم کند، مقدار minValue را باز میگرداند.
با سلام؛ من زمانی که این کد را در SQl 2012 میکنم یک Error در خصوص ToGregorianTime() نمایش داده میشه. لطفا راهنمایی بفرمایید. با تشکر
Msg 6522, Level 16, State 2, Line 1 A .NET Framework error occurred during execution of user-defined routine or aggregate "SpatialDateTime": System.FormatException: String was not recognized as a valid DateTime. System.FormatException: at System.DateTimeParse.ParseExactMultiple(String s, String[] formats, DateTimeFormatInfo dtfi, DateTimeStyles style) at System.DateTime.ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style) at System.Data.SqlTypes.SqlDateTime.Parse(String s) at SpatialDateTime.ToGregorianTime().
ASP.NET MVC از روش بکارگیری binding providerها برای تدارک زیرساخت model binding استفاده میکند که در این روش، دادههای پارامترهای یک action method از طریق هدرها، کوئری استرینگها، بدنهی درخواست و غیره تهیه میشوند. در حالت پیشفرض اگر این پارامترها از نوعهای سادهای مانند اعداد و یا DateTime تشکیل شده باشند و یا به همراه یک TypeConverter باشند که امکان تبدیل این رشته را به آن نوع خاص بدهد، به صورت خودکار bind خواهند شد و هر نوع دیگری، به صورت یک نوع پیچیده درنظر گرفته میشود. نوع پیچیده یعنی bind برای مثال اطلاعات بدنهی درخواست به تک تک خواص آن نوع. برای نمونه در کنترلر زیر:
که از دو مدل زیر استفاده میکند:
میتوان خواص پارامتر days را از طریق کوئری استرینگهای HttpGet، برای مثال با ارائهی آدرس WeatherForecast/GetForecast2?from=1&to=3 به صورت خودکار تامین کرد. زمانیکه اطلاعات رسیده چنین شکلی را داشته باشند، کار پردازش و bind آنها در حالت HttpGet، خودکار است.
روش دیگر پردازش اطلاعات رشتهای رسیده و تشکیل یک Model Binder سفارشی در ASP.NET Core 7x
اکنون فرض کنید بجای آدرس WeatherForecast/GetForecast2?from=1&to=3 که اطلاعات را از طریق کوئری استرینگ مشخص و استانداردی دریافت میکند، میخواهیم اطلاعات آنرا از طریق یک قالب سفارشی و غیراستاندارد مانند WeatherForecast/GetForecast3/1-3 دریافت کنیم:
یکی از راههای انجام اینکار، نوشتن model binderهای سفارشی مخصوص است و یا اکنون در ASP.NET Core 7x میتوان با پیاده سازی اینترفیس IParsable به صورت خودکار و با روشی دیگر به این مقصود رسید:
- برای پیاده سازی این اینترفیس باید دو متد TryParse و Parse آنرا به صورت فوق پیاده سازی کرد و توسط آن، روش تبدیل رشتهی دریافتی از کاربر را به شیء مدنظر، مشخص کرد.
- همینقدر که مدلی IParsable را پیاده سازی کرده باشد، از امکانات آن به صورت خودکار استفاده خواهد شد و نیازی به معرفی و یا تنظیمات خاص دیگری ندارد.
- البته این قابلیت جدید نیست و پشتیبانی از IParsable، پیشتر در Minimal API دات نت 6 ارائه شده بود؛ اما در دات نت 7 توسط ASP.NET Core MVC نیز قابل استفاده شدهاست.
[Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", }; // /WeatherForecast/GetForecast2?from=1&to=3 [HttpGet("[action]")] public IEnumerable<WeatherForecast> GetForecast2(Duration days) { return Enumerable.Range(days.From, days.To) .Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)], }) .ToArray(); } }
public class Duration { public int From { get; set; } public int To { get; set; } } public class WeatherForecast { public DateOnly Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string? Summary { get; set; } }
روش دیگر پردازش اطلاعات رشتهای رسیده و تشکیل یک Model Binder سفارشی در ASP.NET Core 7x
اکنون فرض کنید بجای آدرس WeatherForecast/GetForecast2?from=1&to=3 که اطلاعات را از طریق کوئری استرینگ مشخص و استانداردی دریافت میکند، میخواهیم اطلاعات آنرا از طریق یک قالب سفارشی و غیراستاندارد مانند WeatherForecast/GetForecast3/1-3 دریافت کنیم:
// /WeatherForecast/GetForecast3/1-3 [HttpGet("[action]/{days}")] public IEnumerable<WeatherForecast> GetForecast3(Days days) { return Enumerable.Range(days.From, days.To) .Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)], }) .ToArray(); }
using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace NET7Mvc.Models; public class Days : IParsable<Days> { public Days(int from, int to) { From = from; To = to; } public int From { get; } public int To { get; } public static bool TryParse([NotNullWhen(true)] string? value, IFormatProvider? provider, [MaybeNullWhen(false)] out Days result) { var items = value?.Split('-', StringSplitOptions.RemoveEmptyEntries); if (items is { Length: 2 }) { if (int.TryParse(items[0], NumberStyles.None, provider, out var from) && int.TryParse(items[1], NumberStyles.None, provider, out var to)) { result = new Days(from, to); return true; } } result = default; return false; } public static Days Parse(string value, IFormatProvider? provider) { if (!TryParse(value, provider, out var result)) { throw new ArgumentException("Could not parse the given value.", nameof(value)); } return result; } }
- همینقدر که مدلی IParsable را پیاده سازی کرده باشد، از امکانات آن به صورت خودکار استفاده خواهد شد و نیازی به معرفی و یا تنظیمات خاص دیگری ندارد.
- البته این قابلیت جدید نیست و پشتیبانی از IParsable، پیشتر در Minimal API دات نت 6 ارائه شده بود؛ اما در دات نت 7 توسط ASP.NET Core MVC نیز قابل استفاده شدهاست.