اشتراکها
اشتراکها
سایت Free for developers
اشتراکها
لیستی از منابع DDD
TIO is a family of online interpreters for an evergrowing list of practical and recreational programming languages
اشتراکها
مجموعه Awesome Domain-Driven Design
اشتراکها
منابع مطالعاتی ASP.NET Core
نظرات مطالب
معرفی List Patterns Matching در C# 11
یک نکتهی تکمیلی: ایجاد نوعهای سازگار با List Patterns Matching
در انتهای این مطلب در مورد «سایر نوعهایی که توسط List patterns قابل بررسی هستند» توضیحات مختصری عنوان شد. کامپایلر #C در جهت یافتن نوعهای سازگار با List Patterns Matching، به دنبال اینترفیس خاصی نمیگردد؛ بلکه به دنبال وجود یک سری اعضای خاص، در کلاس مدنظر است و این اعضاء به شرح زیر هستند:
الف) نوع مدنظر باید به همراه یکی از خواص Length و یا Count باشد تا تعداد اعضای مجموعه را مشخص کند. اگر هر دو خاصیت با هم حضور داشته باشند، کامپایلر خاصیت Length را انتخاب میکند:
ب) نوع مجموعهای باید به همراه یک ایندکسر باشد که نوع خروجی آن مهم نیست. اگر در نوع تعریف شده، هر دو امضای زیر وجود داشته باشند، کامپایلر از نمونهی this[Index index] استفاده میکند:
ج) نوع مجموعهای باید از slice pattern، توسط یکی از امضاهای زیر که نوع خروجی آن مهم نیست، پشتیبانی کند. اگر هر دو با هم حضور داشته باشند، کامپایلر از this[System.Range index] استفاده میکند:
برای مثال با توجه به نکات فوق، نوع جدید زیر، با List Patterns Matching سازگاری دارد:
و نمونهای از نحوهی استفادهی از آن به صورت زیر است:
در انتهای این مطلب در مورد «سایر نوعهایی که توسط List patterns قابل بررسی هستند» توضیحات مختصری عنوان شد. کامپایلر #C در جهت یافتن نوعهای سازگار با List Patterns Matching، به دنبال اینترفیس خاصی نمیگردد؛ بلکه به دنبال وجود یک سری اعضای خاص، در کلاس مدنظر است و این اعضاء به شرح زیر هستند:
الف) نوع مدنظر باید به همراه یکی از خواص Length و یا Count باشد تا تعداد اعضای مجموعه را مشخص کند. اگر هر دو خاصیت با هم حضور داشته باشند، کامپایلر خاصیت Length را انتخاب میکند:
public int Length { get; } public int Count { get; }
ب) نوع مجموعهای باید به همراه یک ایندکسر باشد که نوع خروجی آن مهم نیست. اگر در نوع تعریف شده، هر دو امضای زیر وجود داشته باشند، کامپایلر از نمونهی this[Index index] استفاده میکند:
public object this[int index] => throw null; public object this[System.Index index] => throw null;
ج) نوع مجموعهای باید از slice pattern، توسط یکی از امضاهای زیر که نوع خروجی آن مهم نیست، پشتیبانی کند. اگر هر دو با هم حضور داشته باشند، کامپایلر از this[System.Range index] استفاده میکند:
public object this[System.Range index] => throw null; public object Slice(int start, int length) => throw null;
برای مثال با توجه به نکات فوق، نوع جدید زیر، با List Patterns Matching سازگاری دارد:
public class MyListPatternsCompatibleCollection { private readonly List<int> _items = new(); public int Length => _items.Count; public int this[Index index] => _items[index]; public ReadOnlySpan<int> this[Range range] => CollectionsMarshal.AsSpan(_items)[range]; public void Add(int item) => _items.Add(item); }
var collection = new MyListPatternsCompatibleCollection(); collection.Add(1); collection.Add(2); collection.Add(3); _ = collection is [var head, .. var tail];
نوشتن Assert در کدهای تست، وابستگی مستقیم به انتخاب کتابخانه تست دارد. برای مثال:
NUnit:
Microsoft UnitTesting :
»Standard که باید از Should.dll استفاده نمایید؛
»Fluent که باید از Should.Fluent.dll استفاده نمایید؛(پیاده سازی همان فریم ورک Should به صورت Static Reflection)
نصب کتابخانه Should با استفاده از nuget (آخرین نسخه آن در حال حاضر 1.1.20 است ) :
نصب کتابخانه Should.Fluent با استفاده از nuget(آخرین نسخه آن در حال حاضر 1.1.19 است):
در ابتدا همان مثال قبلی را با این کتابخانه بررسی خواهیم کرد:
در نگاه اول چیز خاصی به چشم نمیخورد، اما اگر از این پس قصد داشته باشیم کدهای تست خود را تحت فریم ورک NUnit پیاده سازی کنیم در قسمت Assert کدهای خود هیچ گونه خطایی را مشاهده نخواهیم کرد.
مثال:
در مثال بالا ابتدا با استفاده از Ms UnitTesting دو Assert نوشته شده است سپس در خطوط بعدی همان دو شرط با استفاده از کتابخانه Should نوشتم. در ذیل چند مثال از استفاده این کتابخانه (البته نوع Fluent آن) در هنگام کار با رشته ها، آبجکت ها، boolean و Collectionها را بررسی خواهیم کرد:
#Should.Fluent
#مثالهای استفاده از متغیرهای DateTime و Guid
NUnit:
using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; namespace TestLibrary { [TestFixture] public class MyTest { [Test] public void Test1() { var expectedValue = 2; Assert.That(expectedValue , Is.EqualTo(2)); } } }
Microsoft UnitTesting :
using Microsoft.VisualStudio.TestTools.UnitTesting ; namespace TestLibrary { [TesClass] public class MyTest { [TestMethod] public void Test1() { var expectedValue = 2; Assert.AreEqual (expectedValue , 2); } } }
کدهای Assert نوشته شده در مثال بالا با توجه به فریم ورک مورد استفاده متفاوت است. در حالی که کتابخانه Should، مجموعه ای از Extension Method هاست برای قسمت Assert در UnitTestهای نوشته شده. با استفاده از این کتابخانه دیگر نیازی به نوشتن Assert به سبک و سیاق فعلی نیست. کدهای Assert بسیار خواناتر و قابل درک خواهند بود و از طرفی وابستگی به سایر کتابخانههای تست از بین خواهد رفت.
نکته: مورد استفاده این کتابخانه فقط در قسمت Assert کدهای تست است و استفاده از سایر کتابخانههای جانبی الزامی است.
این کتابخانه به دو صورت مورد استفاده قرار میگیرد:نکته: مورد استفاده این کتابخانه فقط در قسمت Assert کدهای تست است و استفاده از سایر کتابخانههای جانبی الزامی است.
»Standard که باید از Should.dll استفاده نمایید؛
»Fluent که باید از Should.Fluent.dll استفاده نمایید؛(پیاده سازی همان فریم ورک Should به صورت Static Reflection)
نصب کتابخانه Should با استفاده از nuget (آخرین نسخه آن در حال حاضر 1.1.20 است ) :
Install-Package Should
Install-Package ShouldFluent
در ابتدا همان مثال قبلی را با این کتابخانه بررسی خواهیم کرد:
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TestLibrary { [TesClass] public class MyTest { [TestMethod] public void Test1() { var expectedValue = 2; expectedValue.Should().Equal( 2 ); } } }
مثال:
[TestMethod] public void AccountConstructorTest() { const int expectedBalance = 1000; Account bankAccount = new Account(); // Assert.IsNotNull(bankAccount, "Account was null."); // Assert.AreEqual(expectedBalance, bankAccount.AccountBalance, "Account balance not mathcing");
bankAccount.ShouldNotBeNull("Account was null"); bankAccount.AccountBalance.ShouldEqual(expectedBalance, "Account balance not matching"); }
#Should.Fluent
public void Should_fluent_assertions() { object obj = null; obj.Should().Be.Null(); obj = new object(); obj.Should().Be.OfType(typeof(object)); obj.Should().Equal(obj); obj.Should().Not.Be.Null(); obj.Should().Not.Be.SameAs(new object()); obj.Should().Not.Be.OfType<string>(); obj.Should().Not.Equal("foo"); obj = "x"; obj.Should().Not.Be.InRange("y", "z"); obj.Should().Be.InRange("a", "z"); obj.Should().Be.SameAs("x"); "This String".Should().Contain("This"); "This String".Should().Not.Be.Empty(); "This String".Should().Not.Contain("foobar"); false.Should().Be.False(); true.Should().Be.True(); var list = new List<object>(); list.Should().Count.Zero(); list.Should().Not.Contain.Item(new object()); var item = new object(); list.Add(item); list.Should().Not.Be.Empty(); list.Should().Contain.Item(item); };
#مثالهای استفاده از متغیرهای DateTime و Guid
public void Should_fluent_assertions() { var id = new Guid(); id.Should().Be.Empty(); id = Guid.NewGuid(); id.Should().Not.Be.Empty(); var date = DateTime.Now; date1.Should().Be.Today(); var str = ""; str.Should().Be.NullOrEmpty(); var one = "1"; one.Should().Be.ConvertableTo<int>(); var idString = Guid.NewGuid().ToString(); idString.Should().Be.ConvertableTo<Guid>(); }
ASP.NET MVC به همراه HtmlHelper توکاری جهت نمایش یک ChekBoxList نیست؛ اما سیستم Model binder آن، این نوع کنترلها را به خوبی پشتیبانی میکند. برای مثال، یک پروژه جدید خالی ASP.NET MVC را آغاز کنید. سپس یک کنترلر Home جدید را نیز به آن اضافه کنید. در ادامه، برای متد Index آن، یک View خالی را ایجاد نمائید. سپس محتوای این View را به نحو زیر تغییر دهید:
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
@using (Html.BeginForm())
{
<input type='checkbox' name='Result' value='value1' />
<input type='checkbox' name='Result' value='value2' />
<input type='checkbox' name='Result' value='value3' />
<input type="submit" value="submit" />
}
و کنترلر Home را نیز مطابق کدهای زیر ویرایش کنید:
using System.Web.Mvc;
namespace MvcApplication21.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string[] result)
{
return View();
}
}
}
یک breakpoint را در تابع Index دوم که آرایهای را دریافت میکند، قرار دهید. سپس برنامه را اجرا کرده، تعدادی از checkboxها را انتخاب و فرم نمایش داده شده را به سرور ارسال کنید:
بله. همانطور که ملاحظه میکنید، تمام عناصر ارسالی انتخاب شده که دارای نامی مشابه بودهاند، به یک آرایه قابل بایند هستند و سیستم model binder میداند که چگونه باید این اطلاعات را دریافت و پردازش کند.
از این مقدمه میتوان به عنوان پایه و اساس نوشتن یک HtmlHelper سفارشی CheckBoxList استفاده کرد.
برای این منظور یک پوشه جدید را به نام app_code، به ریشه پروژه اضافه نمائید. سپس یک فایل خالی را به نام Helpers.cshtml نیز به آن اضافه کنید. محتوای این فایل را به نحو زیر تغییر دهید:
@helper CheckBoxList(string name, List<System.Web.Mvc.SelectListItem> items)
{
<div class="checkboxList">
@foreach (var item in items)
{
@item.Text
<input type="checkbox" name="@name"
value="@item.Value"
@if (item.Selected) { <text>checked="checked"</text> }
/>
< br />
}
</div>
}
و برای استفاده از آن، کنترلر Home را مطابق کدهای زیر ویرایش کنید:
using System.Collections.Generic;
using System.Web.Mvc;
namespace MvcApplication21.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
ViewBag.Tags = new List<SelectListItem>
{
new SelectListItem { Text = "Item1", Value = "Val1", Selected = false },
new SelectListItem { Text = "Item2", Value = "Val2", Selected = false },
new SelectListItem { Text = "Item3", Value = "Val3", Selected = true }
};
return View();
}
[HttpPost]
public ActionResult GetTags(string[] tags)
{
return View();
}
[HttpPost]
public ActionResult Index(string[] result)
{
return View();
}
}
}
و در این حالت View برنامه به شکل زیر درخواهد آمد:
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
@using (Html.BeginForm())
{
<input type='checkbox' name='Result' value='value1' />
<input type='checkbox' name='Result' value='value2' />
<input type='checkbox' name='Result' value='value3' />
<input type="submit" value="submit" />
}
@using (Html.BeginForm(actionName: "GetTags", controllerName: "Home"))
{
@Helpers.CheckBoxList("Tags", (List<SelectListItem>)ViewBag.Tags)
<input type="submit" value="submit" />
}
با توجه به اینکه کدهای Razor قرار گرفته در پوشه خاص app_code در ریشه سایت، به صورت خودکار در حین اجرای برنامه کامپایل میشوند، متد Helpers.CheckBoxList در تمام Viewهای برنامه در دسترس خواهد بود. در این متد، یک نام و لیستی از SelectListItemها دریافت میگردد. سپس به صورت خودکار یک CheckboxList را تولید خواهد کرد. برای دریافت مقادیر ارسالی آن به سرور هم باید مطابق متد GetTags تعریف شده در کنترلر Home عمل کرد. در اینجا Value عناصر انتخابی به صورت آرایهای از رشتهها در دسترس خواهد بود.
روشی جامعتر
در آدرس زیر میتوانید یک HtmlHelper بسیار جامع را جهت تولید CheckBoxList در ASP.NET MVC بیابید. در همان صفحه روش استفاده از آن، به همراه چندین مثال ارائه شده است:
https://github.com/devnoob/MVC3-Html.CheckBoxList-custom-extension