وبلاگها و سایتهای ایرانی
- قسمت دوم مروری بر GDI+ (این دوستان هم تاریخ RSS اشان شمسی است)
Visual Studio
ASP. Net
طراحی وب
اسکیوال سرور
به روز رسانیها
سیشارپ
عمومی دات نت
PHP
ویندوز
گوگل
متفرقه
public class UserFactory { public User CreateUser(string email) { return new User(email); } }
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace ValueOf { public class ValueOf<TValue, TThis> where TThis : ValueOf<TValue, TThis>, new() { private static readonly Func<TThis> Factory; /// <summary> /// WARNING - THIS FEATURE IS EXPERIMENTAL. I may change it to do /// validation in a different way. /// Right now, override this method, and throw any exceptions you need to. /// Access this.Value to check the value /// </summary> protected virtual void Validate() { } static ValueOf() { ConstructorInfo ctor = typeof(TThis) .GetTypeInfo() .DeclaredConstructors .First(); var argsExp = new Expression[0]; NewExpression newExp = Expression.New(ctor, argsExp); LambdaExpression lambda = Expression.Lambda(typeof(Func<TThis>), newExp); Factory = (Func<TThis>)lambda.Compile(); } public TValue Value { get; protected set; } public static TThis From(TValue item) { TThis x = Factory(); x.Value = item; x.Validate(); return x; } protected virtual bool Equals(ValueOf<TValue, TThis> other) { return EqualityComparer<TValue>.Default.Equals(Value, other.Value); } public override bool Equals(object obj) { if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; return obj.GetType() == GetType() && Equals((ValueOf<TValue, TThis>)obj); } public override int GetHashCode() { return EqualityComparer<TValue>.Default.GetHashCode(Value); } public static bool operator ==(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b) { if (a is null && b is null) return true; if (a is null || b is null) return false; return a.Equals(b); } public static bool operator !=(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b) { return !(a == b); } public override string ToString() { return Value.ToString(); } } }
public class EmailAddress : ValueOf<string, EmailAddress> { }
EmailAddress emailAddress = EmailAddress.From("foo@bar.com");
public class Address : ValueOf<(string firstLine, string secondLine, Postcode postcode), Address> { }
public class Person { public string FirstName { get; set; } public Person(string firstName) { this.FirstName = firstName; } }
public class Person { public string FirstName { get; set; } public Person(string firstName) => this.FirstName = firstName; }
public class Person { public string Name { get; } public int Age { get; } public Person(string name, int age) => (Name, Age) = (name, age); }
public class Person { private readonly (string name, int age) _tuple; public string Name => _tuple.name; public int Age => _tuple.age; public Person(string name, int age) => _tuple = (name, age); }
public class Resource { ~Resource() => Console.WriteLine("destructor"); }
private int _x; public int X { get { return _x; } set { _x = value; } }
private int _x; public int X { get => _x; set => _x = value; }
همچنین برای Event Accessors نیز میتوانیم از این قابلیت استفاده کنیم:
private EventHandler _someEvent; public event EventHandler SomeEvent { add => _someEvent += value; remove => _someEvent -= value; }
public class TestProfile1 : Profile { protected override void Configure() { // این تنظیم سراسری هست و به تمام خواص زمانی اعمال میشود this.CreateMap<DateTime, string>().ConvertUsing(new DateTimeToPersianDateTimeConverter()); this.CreateMap<User, UserViewModel>(); // Other mappings } public override string ProfileName { get { return this.GetType().Name; } } }
public interface IMapFrom<T> { } public interface IHaveCustomMappings { void CreateMappings(IConfiguration configuration); }
public class PersonViewModel : IMapFrom<Person> { public string Name { get; set; } public string LastName { get; set; } }
public class PersonViewModel : IHaveCustomMapping { public string Name { get; set; } // دیگر پراپرتیها public void CreateMappings(IConfiguration configuration) { configuration.CreateMap<ApplicationUser, PersonViewModel>() .ForMember(m => m.Name, opt => opt.MapFrom(u => u.ApplicationUser.UserName)); // دیگر نگاشتها } }
public void Execute() { var types = Assembly.GetExecutingAssembly().GetExportedTypes(); LoadStandardMappings(types); LoadCustomMappings(types); }
private static void LoadStandardMappings(IEnumerable <Type> types) { var maps = (from t in types from i in t.GetInterfaces() where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom< >) && !t.IsAbstract && !t.IsInterface select new { Source = i.GetGenericArguments()[0], Destination = t }).ToArray(); foreach(var map in maps) { Mapper.CreateMap(map.Source, map.Destination); } }
private static void LoadCustomMappings(IEnumerable <Type> types) { var maps = (from t in types from i in t.GetInterfaces() where typeof(IHaveCustomMappings).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface select(IHaveCustomMappings) Activator.CreateInstance(t)).ToArray(); foreach(var map in maps) { map.CreateMappings(Mapper.Configuration); } }
PM> Install-Package structuremap
namespace DI03.Services { public interface IUsersService { string GetUserEmail(int userId); } } namespace DI03.Services { public interface IEmailsService { void SendEmailToUser(int userId, string subject, string body); } } using System; namespace DI03.Services { public class UsersService : IUsersService { public UsersService() { //هدف صرفا نمایش وهله سازی خودکار این وابستگی است Console.WriteLine("UsersService ctor."); } public string GetUserEmail(int userId) { //برای مثال دریافت از بانک اطلاعاتی و بازگشت یک نمونه جهت آزمایش برنامه return "name@site.com"; } } } using System; namespace DI03.Services { public class EmailsService: IEmailsService { private readonly IUsersService _usersService; public EmailsService(IUsersService usersService) { Console.WriteLine("EmailsService ctor."); _usersService = usersService; } public void SendEmailToUser(int userId, string subject, string body) { var email = _usersService.GetUserEmail(userId); Console.WriteLine("SendEmailTo({0})", email); } } }
using DI03.Services; using StructureMap; namespace DI03 { class Program { static void Main(string[] args) { // تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود ObjectFactory.Initialize(x => { x.For<IEmailsService>().Use<EmailsService>(); x.For<IUsersService>().Use<UsersService>(); }); //نمونهای از نحوه استفاده از تزریق وابستگیهای خودکار var emailsService = ObjectFactory.GetInstance<IEmailsService>(); emailsService.SendEmailToUser(userId: 1, subject: "Test", body: "Hello!"); } } }
UsersService ctor. EmailsService ctor. SendEmailTo(name@site.com)
using System; namespace DI03.Services { public class UsersService : IUsersService { private int _i; public UsersService() { //هدف صرفا نمایش وهله سازی خودکار این وابستگی است Console.WriteLine("UsersService ctor."); } public string GetUserEmail(int userId) { _i++; Console.WriteLine("i:{0}", _i); //برای مثال دریافت از بانک اطلاعاتی و بازگشت یک نمونه جهت آزمایش برنامه return "name@site.com"; } } }
//نمونهای از نحوه استفاده از تزریق وابستگیهای خودکار var emailsService1 = ObjectFactory.GetInstance<IEmailsService>(); emailsService1.SendEmailToUser(userId: 1, subject: "Test1", body: "Hello!"); var emailsService2 = ObjectFactory.GetInstance<IEmailsService>(); emailsService2.SendEmailToUser(userId: 1, subject: "Test2", body: "Hello!");
UsersService ctor. EmailsService ctor. i:1 SendEmailTo(name@site.com) UsersService ctor. EmailsService ctor. i:1 SendEmailTo(name@site.com)
// تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود ObjectFactory.Initialize(x => { x.For<IEmailsService>().Use<EmailsService>(); x.For<IUsersService>().Singleton().Use<UsersService>(); });
UsersService ctor. EmailsService ctor. i:1 SendEmailTo(name@site.com) EmailsService ctor. i:2 SendEmailTo(name@site.com)
Singleton() HttpContextScoped() HybridHttpOrThreadLocalScoped()
protected void Application_EndRequest(object sender, EventArgs e) { ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); }
// تنظیمات اولیه برنامه که فقط یکبار باید در طول عمر برنامه انجام شود ObjectFactory.Initialize(x => { //x.For<IEmailsService>().Use<EmailsService>(); //x.For<IUsersService>().Singleton().Use<UsersService>(); x.Scan(scan => { scan.AssemblyContainingType<IEmailsService>(); scan.WithDefaultConventions(); }); });
function PersonType(name) { this.name = name; } PersonType.prototype.sayName = function() { console.log(this.name); }; let person = new PersonType("Nicholas"); person.sayName(); // outputs "Nicholas" console.log(person instanceof PersonType); // true console.log(person instanceof Object); // true
class PersonClass { // equivalent of the PersonType constructor constructor(name) { this.name = name; } // equivalent of PersonType.prototype.sayName sayName() { console.log(this.name); } } let person = new PersonClass("Nicholas"); person.sayName(); // outputs "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"
//unnamed class expressions do not require identifiers after "class" let PersonClass = class { // equivalent of the PersonType constructor constructor(name) { this.name = name; } // equivalent of PersonType.prototype.sayName sayName() { console.log(this.name); } }; let person = new PersonClass("Nicholas"); person.sayName(); // outputs "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function" //named let PersonClass = class PersonClass2 { // equivalent of the PersonType constructor constructor(name) { this.name = name; } // equivalent of PersonType.prototype.sayName sayName() { console.log(this.name); } }; console.log(PersonClass === PersonClass2); // true
function createObject(classDef) { return new classDef(); } let obj = createObject(class { sayHi() { console.log("Hi!"); } }); obj.sayHi(); // "Hi!"
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}("Nicholas");
person.sayName(); // "Nicholas
در کد بالا ، "Nicholas" به عنوان آرگومان سازنده کلاس بی نام در هنگام ساخت نمونه از طریق پرانتزهای باز و بسته انتهایی، پاس داده شده است. استفاده از class declarations یا class expressions برای کار با کلاسها به سبک کاری شما مربوط خواهد شد و بس. ولی نکته این است که هر دو شکل پیاده سازی کلاسها بر خلاف function declarations و function expressions ، قابلیت hoisting را نخواهند داشت و به صورت پیش فرض در حالت strict mode اجرا خواهند شد.
Accessor Properties
کلاسها این امکان را دارند تا بتوان برای پراپرتیهایی که در سازندهی کلاس تعریف شدهاند، accessor property تعریف کرد. سینتکس استفاده شدهی برای این منظور، شبیه به ساخت object literal accessorها در ES 5 میباشد.برای مثال:
class CustomHTMLElement { constructor(element) { this.element = element; } get html() { return this.element.innerHTML; } set html(value) { this.element.innerHTML = value; } } var descriptor = Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype,\ "html"); console.log("get" in descriptor); // true console.log("set" in descriptor); // true
در کد بالا ، getter و setter برای محتوای html مربوط به پراپرتی element در نظر گرفته شده است که در واقعا نمایندگان (delegates) مربوط به متد innterHTML خود element میباشند. معادل همین پیاده سازی بدون استفاده از سینتکس کلاس، به شکل زیر خواهد بود:
// direct equivalent to previous example let CustomHTMLElement = (function() { "use strict"; const CustomHTMLElement = function(element) { // make sure the function was called with new if (typeof new.target === "undefined") { throw new Error("Constructor must be called with new."); } this.element = element; } Object.defineProperty(CustomHTMLElement.prototype, "html", { enumerable: false, configurable: true, get: function() { return this.element.innerHTML; }, set: function(value) { this.element.innerHTML = value; } }); return CustomHTMLElement; }());
حتما متوجه شدید که با استفاده از سینتکس کلاس برای تعریف accessor propertyها حجم کد نویسی شما خیلی کاهش خواهد یافت و این تنها تفاوت بین دو شکل پیاده سازی فوق میباشد.
Static Members
ساخت اعضای استاتیک در ورژن قبل برای مثال به شکل زیر بود:
function PersonType(name) { this.name = name; } // static method PersonType.create = function(name) { return new PersonType(name); }; // instance method PersonType.prototype.sayName = function() { console.log(this.name); }; var person = PersonType.create("Nicholas");
در کد بالا یک متد استاتیک برای نوع داده شخصی PersonType در نظر گرفته شده است. این مورد در ES 6 بهبود یافته و فقط با قرار دادن کلمهی کلیدی static قبل از نام متد و یا accessor property میتوان به نتیجهی مثال بالا دست یافت:
class PersonClass { // equivalent of the PersonType constructor constructor(name) { this.name = name; } // equivalent of PersonType.prototype.sayName sayName() { console.log(this.name); } // equivalent of PersonType.create static create(name) { return new PersonClass(name); } } let person = PersonClass.create("Nicholas");
نکته این که نمیتوان سازندهی استاتیک در کلاس خود تعریف کرد.
Inheritance
مشکل دیگری که در ES 5 برای پیاده سازی انواع داده شخصی وجود داشت، حجم بالای کد و مراحلی بود که برای پیاده سازی وراثت میبایستی متحمل میشدیم. برای مثال در ورژن قبلی باید به شکل زیر عمل میکردیم:
function Rectangle(length, width) { this.length = length; this.width = width; } Rectangle.prototype.getArea = function() { return this.length * this.width; }; function Square(length) { Rectangle.call(this, length, length); } Square.prototype = Object.create(Rectangle.prototype, { constructor: { value:Square, enumerable: true, writable: true, configurable: true } }); var square = new Square(3); console.log(square.getArea()); // 9 console.log(square instanceof Square); // true console.log(square instanceof Rectangle); // true
درکد بالا Square از Rectangle ارث بری کرده که برای این منظور Square.prototype را با ساخت نمونهای از Rectangle.prototype بازنویسی کردهایم. این سینتکس باعث سردرگمی اغلب تازه کاران خواهد شد. برای این منظور در ES 6 خیلی راحت با استفاده از کلمهی کلیدی extends بعد از نام کلاس و سپس نوشتن نام کلاس پایه خواهیم توانست به نتیجهی بالا دست یابیم. به عنوان مثال:
class Rectangle { constructor(length, width) { this.length = length; this.width = width; } getArea() { return this.length * this.width; } } class Square extends Rectangle { constructor(length) { // same as Rectangle.call(this, length, length) super(length, length); } } var square = new Square(3); console.log(square.getArea()); // 9 console.log(square instanceof Square); // true console.log(square instanceof Rectangle); // true
در کد بالا نیز کلاس Square از کلاس Rectangle ارث بری کرده و همانطور که مشخص است و انتظار داشتیم، متد getArea در یکی از اعضای به ارث برده شده از کلاس پایه، قابل دسترسی میباشد. در سازندهی کلاس Square با استفاده از ()super توانستهایم سازندهی کلاس Rectangle را با آرگومانهای مشخصی فراخوانی کنیم.
اگر برای subclass، سازنده در نظر گرفته شود، باید سازندهی کلاس پیاده سازی کننده حتما فراخوانی شود. در غیر این صورت با خطا روبرو خواهید شد. ولی در مقابل اگر هیچ سازندهای برای subclass در نظر نگرفته باشید، به صورت خودکار سازندهی کلاس پایه هنگام ساخت نمونه از این subclass فراخوانی خواهد شد:
class Square extends Rectangle { // no constructor } // Is equivalent to class Square extends Rectangle { constructor(...args) { super(...args); } }
همانطور که در کد بالا مشخص است اگر سازندهای برای subclass در نظر گرفته نشود، تمام آرگومانهای ارسالی، هنگام نمونه سازی از آن، به ترتیب به سازندهی کلاس پایه نیز پاس داده خواهند شد.
چند نکته
- فقط زمانی میتوان ()super را فراخوانی کرد که از بعد از نام کلاس از کلمهی کلیدی extends استفاده شده باشد.
- باید قبل از دسترسی به کلمهی کلیدی this در سازنده subclass، سازندهی کلاس پایه را با استفاده از ()super فراخوانی کرد.
Class Methods
اگر در subclass متدی همنام متد کلاس پایه داشته باشید، به صورت خودکار متد کلاس پایه override خواهد شد. البته همیشه میتوان متد کلاس پایه را مستقیم هم فراخوانی کرد؛ به عنوان مثال:
class Square extends Rectangle { constructor(length) { super(length, length); } // override, shadow, and call Rectangle.prototype.getArea() getArea() { return super.getArea(); } }
در کد بالا متد getArea کلاس پایه بازنویسی شده است. ولی با این حال با استفاده از کلمهی super به متد اصلی در کلاس پایه دسترسی داریم.
نام متدها حتی میتوانند قابلیت محاسباتی داشته باشند. به عنوان مثال خواهید توانست به شکل زیر عمل کنید:
let methodName = "getArea"; class Square extends Rectangle { constructor(length) { super(length, length); } // override, shadow, and call Rectangle.prototype.getArea() [methodName]() { return super.getArea(); } }
کد بالا دقیقا با مثال قبل یکسان است با این تفاوت که نام متد getArea را به صورت رشتهای با قابلیت محاسباتی در نظر گرفتیم.
ارث بردن اعضای استاتیک یک مفهوم جدید در جاوااسکریپت میباشد که نمونهی آن را میتوانید در کد زیر مشاهده کنید:
class Rectangle { constructor(length, width) { this.length = length; this.width = width; } getArea() { return this.length * this.width; } static create(length, width) { return new Rectangle(length, width); } } class Square extends Rectangle { constructor(length) { // same as Rectangle.call(this, length, length) super(length, length); } } var rect = Square.create(3, 4); console.log(rect instanceof Rectangle); // true console.log(rect.getArea()); // 12 console.log(rect instanceof Square); // false
در کد بالا متد استاتیک create یک متد استاتیک در کلاس پایه Rectangle میباشد که این بار در کلاس Square هم قابل دسترسی است.
قدرتمندترین جنبهی کلاسهای مشتق شده در ES 6 ، توانایی ارث بری از expressionها میباشد. شما میتوانید کلمهی کلیدی extends را با هر expression ای استفاده کنید. برای مثال:
function Rectangle(length, width) { this.length = length; this.width = width; } Rectangle.prototype.getArea = function() { return this.length * this.width; }; class Square extends Rectangle { constructor(length) { super(length, length); } } var x = new Square(3); console.log(x.getArea()); // 9 console.log(x instanceof Rectangle); // true
در کد بالا Rectangle یک تابع سازنده برای تعریف نوع داده شخصی در ES 5 و Square، نوع داده با سینتکس کلاس در ES 6 میباشند. ولی با این حال کلاس Square توانسته است از Rectangle ارث بری کند.
یکی دیگر از امکانات فوق العادهی آن، مشخص کردن داینامیک کلاس پایه است. برای مثال:
function Rectangle(length, width) { this.length = length; this.width = width; } Rectangle.prototype.getArea = function() { return this.length * this.width; }; function getBase() { return Rectangle; } class Square extends getBase() { constructor(length) { super(length, length); } } var x = new Square(3); console.log(x.getArea()); // 9 console.log(x instanceof Rectangle); // true
در کد بالا متد getBase میتواند شامل منطق بیشتری هم برای مشخص کردن داینامیک کلاس پایه باشد که این مورد در بعضی از سناریوها مفید خواهد بود.
new.target
با استفاده از مقدار موجود در این شیء، در سازندهی کلاس میتوان مشخص کرد که به چه شکلی به کلاس مورد نظر استناد شدهاست. برای مثال:
class Rectangle { constructor(length, width) { console.log(new.target === Rectangle); this.length = length; this.width = width; } } // new.target is Rectangle var obj = new Rectangle(3, 4); // outputs true
در کد بالا با استفاده از new.target توانستیم که مشخص کنیم شیء ایجاد شده از نوع Rectangle میباشد. با استفاده از این امکان خوب میتوان به ساخت کلاسهای abstract رسید. برای مثال:
// abstract base class class Shape { constructor() { if (new.target === Shape) { throw new Error("This class cannot be instantiated directly.") } } } class Rectangle extends Shape { constructor(length, width) { super(); this.length = length; this.width = width; } } var x = new Shape(); // throws error var y = new Rectangle(3, 4); // no error console.log(y instanceof Shape); // true
در کد بالا که کاملا هم مشخص است؛ در سازندهی کلاس Shape مشخص کردهایم که اگر مستقیما از کلاس Shape نمونه سازی شد، یک exception را پرتاب کند. با این اوصاف ما توانستهایم که کلاس Shape را به صورت Abstract معرفی کنیم.
namespace System { public interface IServiceProvider { object GetService(Type serviceType); } }
using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; namespace CoreIocServices { public interface IProductService { void Delete(int id); } public class ProductService : IProductService { private readonly ITestService _testService; private readonly ILogger<ProductService> _logger; public ProductService(IServiceProvider serviceProvider) { _testService = serviceProvider.GetRequiredService<ITestService>(); _logger = serviceProvider.GetService<ILogger<ProductService>>() ?? NullLogger<ProductService>.Instance; } public void Delete(int id) { _testService.Run(); _logger.LogInformation($"Deleted a product with id = {id}"); } } }
namespace Microsoft.Extensions.DependencyInjection { public static class ServiceProviderServiceExtensions { public static T GetRequiredService<T>(this IServiceProvider provider); public static T GetService<T>(this IServiceProvider provider); } }
تگ اولیه | تگ ثانویه | فرهنگ مربوطه |
De | - | آلمانی |
De | AT | آلمانی اتریشی |
De | CH | آلمانی سوئیسی |
En | - | انگلیسی |
En | GB | انگلیسی بریتانیا |
En | US | انگلیسی آمریکایی |
/c[ulture]:En-US
C:\MyApp
C:\MyApp\en-US
System.Resources.ResourceManager
System.Reflection.AssemblyCultureAttribute =============== [assembly:AssemblyCulture("de-CH")]