public class Model { public string Photo { get; set; } }
public class viewModel { public IFormFile Photo { get; set; } }
public class Model { public string Photo { get; set; } }
public class viewModel { public IFormFile Photo { get; set; } }
public class Employee { public string FirstName { get; set; } public string LastName { get; set; } public DateTime HireDate { get; set; } public decimal Salary { get; set; } public Employee(string firstName, string lastName, DateTime hireDate, decimal salary) { FirstName = firstName; LastName = lastName; HireDate = hireDate; Salary = salary; } }
public class Employee(string firstName, string lastName, DateTime hireDate, decimal salary) { public string FirstName { get; set; } = firstName; public string LastName { get; set; } = lastName; public DateTime HireDate { get; set; } = hireDate; public decimal Salary { get; set; } = salary; }
var employee = new Employee("John", "Doe", new DateTime(2020, 1, 1), 50000);
public record class Book(string Title, string Publisher);
public record struct Color(int R, int G, int B);
public class MyService { private readonly IDepedent _dependent; public MyService(IDependent dependent) { _dependent = dependent; } public void Do() { _dependent.DoWork(); } }
public class MyService(IDependent dependent) { public void Do() { dependent.DoWork(); } }
using System; using System.Diagnostics; using System.Runtime.CompilerServices; namespace CS8Tests { [NullableContext(1)] [Nullable(0)] public class Employee { [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string <FirstName>k__BackingField; [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string <LastName>k__BackingField; [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private DateTime <HireDate>k__BackingField; [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private Decimal <Salary>k__BackingField; public Employee(string firstName, string lastName, DateTime hireDate, Decimal salary) { this.<FirstName>k__BackingField = firstName; this.<LastName>k__BackingField = lastName; this.<HireDate>k__BackingField = hireDate; this.<Salary>k__BackingField = salary; base..ctor(); } public string FirstName { [CompilerGenerated] get { return this.<FirstName>k__BackingField; } [CompilerGenerated] set { this.<FirstName>k__BackingField = value; } } public string LastName { [CompilerGenerated] get { return this.<LastName>k__BackingField; } [CompilerGenerated] set { this.<LastName>k__BackingField = value; } } public DateTime HireDate { [CompilerGenerated] get { return this.<HireDate>k__BackingField; } [CompilerGenerated] set { this.<HireDate>k__BackingField = value; } } public Decimal Salary { [CompilerGenerated] get { return this.<Salary>k__BackingField; } [CompilerGenerated] set { this.<Salary>k__BackingField = value; } } } }
public class Person(string firstName, string lastName) { public Person() : this("John", "Smith") { } public Person(string firstName) : this(firstName, "Smith") { } public string FullName => $"{firstName} {lastName}"; }
public class User { public User(string firstName, string lastName) { } } public class Editor(string firstName, string lastName) : User { }
public class Automobile { public Automobile(int wheels, int seats) { Wheels = wheels; Seats = seats; } public int Wheels { get; } public int Seats { get; } } public class Car : Automobile { public Car(int seats) : base(4, seats) { } }
public class Automobile(int wheels, int seats) { public int Wheels { get; } = wheels; public int Seats { get; } = seats; } public class Car(int seats) : Automobile(4, seats);
public class MyBaseClass(string s); // no body required public class Derived(int i, string s, bool b) : MyBaseClass(s) { public int I { get; set; } = i; public string B => b.ToString(); }
public class Human(int age) { // initialization public int Age { get; set; } = age; // capture public string Bio => $"My age is {age}!"; }
var p = new Human(42); Console.WriteLine(p.Age); // Output: 42 Console.WriteLine(p.Bio); // Output: My age is 42! p.Age++; Console.WriteLine(p.Age); // Output: 43 Console.WriteLine(p.Bio); // Output: My age is 42! // !
using System.Diagnostics; using System.Runtime.CompilerServices; namespace CS8Tests { [NullableContext(1)] [Nullable(0)] public class Human { [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int <age>P; [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int <Age>k__BackingField; public Human(int age) { this.<age>P = age; this.<Age>k__BackingField = this.<age>P; base..ctor(); } public int Age { [CompilerGenerated] get { return this.<Age>k__BackingField; } [CompilerGenerated] set { this.<Age>k__BackingField = value; } } public string Bio { get { DefaultInterpolatedStringHandler interpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 1); interpolatedStringHandler.AppendLiteral("My age is "); interpolatedStringHandler.AppendFormatted<int>(this.<age>P); interpolatedStringHandler.AppendLiteral("!"); return interpolatedStringHandler.ToStringAndClear(); } } } }
public Human(int age) { this.<age>P = age; this.<Age>k__BackingField = this.<age>P; base..ctor(); }
public class Contact { public int Id { get; set; } [AllowHtml] public string Name { get; set; } public string Address { get; set; } }
[HttpGet] public ActionResult ContactUs() { return View(); } [HttpPost] public ActionResult ContactUs(Contact con) { ViewBag.Name = con.Name.ToSafeHtml(); ViewBag.MyAddress = con.Address; return View(); }
A potentially dangerous Request.Form value was detected from the client (Name="<script>alert("aaa")...").
اصل چهارم: Starve for loosely coupled designs
"به دنبال طراحی با اتصال سست بین اجزا باش"
اتصال بین اجزای برنامه نویسی باعث سختتر شدن مدیریت تغییرات میشود؛ چرا که با تغییر یک بخش، بخشهای متصل نیز دچار مشکل خواهند شد. اتصالها از لحاظ نوع قدرت متفاوتند و اساسا سیستمی بدون اتصال وجود ندارد. لذا باید به دنبال یک طراحی با کمترین میزان قدرت اتصال یا همان سست اتصال باشیم.
تا به اینجا، اصلهای دوم و سوم ما را در کاهش وابستگی و اتصال قوی کمک کردهاند. استفاده از واسطها، باعث کاهش وابستگی به نوع پیاده سازی میشود. استفاده از ترکیب نیز به نوعی باعث از بین رفتن وابستگی قوی بین کلاسهای فرزند و کلاس والد میشود و با روشی دیگر (استفاده از شیء در برگرفته شده برای پیاده سازی وظیفهی تغییر کننده) وظایف را در کلاسها پیاده سازی میکند. در زیر نمونهی اتصال قوی و نتیجهی آن را میبینیم:
public class StrongCoupledConcreteA { public string GenerateString(string s) { return s + " from" + this.GetType().ToString(); } } public class StrongCoupledConcreteB { public void GenerateString(ref string s) { s += " from" + this.GetType().ToString(); } } public class Printer { bool condition; public Printer(bool cond) { condition = cond; } public void SetCondition(bool value) { condition = value; } public void Print() { string result; string input = " this message is"; if (condition) { var stringGenerator = new StrongCoupledConcreteA(); result = stringGenerator.GenerateString(input); } else { var stringGenerator = new StrongCoupledConcreteB(); result = input; stringGenerator.GenerateString(ref result); } Console.WriteLine(result); } } public class Context { Printer printer; public void DoWork() { printer = new Printer(true); printer.Print(); printer.SetCondition(false); printer.Print(); } }
حال کد بازنویسی شده را با آن مقایسه کنید:
public interface IStringGenerator { string GenerateString(string s); } public class LooslyCoupledConcreteA : IStringGenerator { public string GenerateString(string s) { return s + " from " + this.GetType().ToString(); } } public class LooslyCoupledConcreteB : IStringGenerator { public string GenerateString(string s) { return s + " from " + this.GetType().ToString(); } } public class Printer { bool condition; public Printer(bool cond) { condition = cond; } public void SetCondition(bool value) { condition = value; } public void Print() { string result; string input = " this message is"; IStringGenerator generator; if (condition) { generator = new LooslyCoupledConcreteA(); } else { generator = new LooslyCoupledConcreteB(); } result = generator.GenerateString(input); Console.WriteLine(result); } }
با کمی دقت مشاهده میکنیم که در کلاسهای strongly coupled با اینکه هدف هر دو کلاس تولید یک رشته است، ولی عدم وجود پروتکل باعث شده است نحوهی گرفتن ورودی و برگرداندن خروجی متفاوت شود و در نتیجه نیازمند به اضافه کردن پیچیدگی در کلاس فراخوانی کنندهی آنها میشویم. این در حالی است که در روش loosely coupled با ایجاد یک پروتکل (واسط IStringGenerator ) این پیچیدگی از بین رفته است. در اینجا نوع اتصال (وابستگی) از جنس اتصال (وابستگی) قوی به تعریف (prototype) و شاید به نوعی نحوهی پیاده سازی متد میباشد.
SOLID Principles *
پنج اصل بعدی به اصول SOLID معروف هستند.
S: Single Responsibility
O: Open/Closed
L: Liskov’s Substitution
I: Interface Segregation
D: Dependency Injection
اصل پنجم: Single responsibility
"به دنبال ماژولهای تک مسئولیتی باش"
در این قسمت مقصود از مسئولیت، «دلیلی است که کلاس باید تغییر کند» بدین معنا که اگر کلاسی با چند دلیل متفاوت مجبور به تغییر شود، آن کلاس چند مسئولیتی است. کلاسهای چند مسئولیتی عموما کد حجیمی دارند؛ نام آنها تعریف دقیقی را از مسئولیتشان ارائه نمیدهد و با عنوانی بسیار کلی نامگذاری میشوند و اشکال زدایی آنها بسیار طاقت فرساست. از طرفی، چند مسئولیتی بودن یک کلاس، باعث از بین رفتن مزایای توارث میشود. مثلا فرض کنید دو مسئولیت A,B در واسطی بیان میشوند که به یکدیگر مرتبط نبوده و مستقلند. برای مسئولیت A دو پیاده سازی و برای مسئولیت B، سه پیاده سازی در نظر گرفته شده است و جمعا برای پشتیبانی از تمامی حالات باید شش کلاس پیاده ساز، در نظر گرفته شود که توارث را سخت و بی معنی میکند زیرا قابلیت استفاده مجدد را از توارث سلب کرده است. با این وجود عملا رعایت همچین نکتهای در دنیای واقعی کار سختی است.
مثال زیر این مشکل را بیان میدارد:
// single responsibility principle - bad example interface IEmail { void SetSender(string sender); void SetReceiver(string receiver); void SetContent(string content); } class Email : IEmail { public void SetSender(string sender) { throw new NotImplementedException(); } public void SetReceiver(string receiver) { throw new NotImplementedException(); } public void SetContent(string content) { throw new NotImplementedException(); } }
در این مثال کلاس Email دارای دو مسئولیت (دلیل برای تغییر) است: الف- نحوه مقداردهی فرستنده و گیرنده براساس پروتکلهای مختلف مانند IMAP, POP3 ، بدین معنا که با تغییر پروتکل نیاز به تغییر پیاده سازی خواهیم شد. ب- تعریف محتوای پیام، بدین معنا که برای پشتیبانی از محتوای html, xml نیاز به تغییر کلاس Email داریم.
با تغییر طراحی خواهیم داشت:
// single responsibility principle - good example public interface IMessage { void SetSender(string sender); void SetReceiver(string receiver); void SetContent(IContent content); } public interface IContent { string GetAsString(); // used for serialization } public class Email : IMessage { public void SetSender(string sender) { throw new NotImplementedException(); } public void SetReceiver(string receiver) { throw new NotImplementedException(); } public void SetContent(IContent content) { throw new NotImplementedException(); } }
در اینجا واسط IContent مسئولیت پشتیبانی از xml, html را
خواهد داشت و نیازی به تغییر کلاس Email برای
پشتیبانی از این فرمتهای محتوای پیام را نخواهیم داشت.
اصل ششم: Open for
extension, close for modification : Open/Closed Principle
"پذیرای توسعه و
بازدارنده از تغییر هر آنچه که هست، باش"
ا ین اصل میگوید طراحی باید به گونهای باشد که با
اضافه شدن یک ویژگی، کدهای قبلی تغییری نکنند و فقط کدهای جدید برای پیاده سازی
ویژگی جدید نوشته شوند.
public class AreaCalculator { public double Area(object[] shapes) { double area = 0; foreach (var shape in shapes) { if (shape is Square) { Square square = (Square)shape; area += Math.Sqrt(square.Height); } if (shape is Triangle) { Triangle triangle = (Triangle)shape; double TotalHalf = (triangle.FirstSide + triangle.SecondSide + triangle.ThirdSide) / 2; area += Math.Sqrt(TotalHalf * (TotalHalf - triangle.FirstSide) * (TotalHalf - triangle.SecondSide) * (TotalHalf - triangle.ThirdSide)); } if (shape is Circle) { Circle circle = (Circle)shape; area += circle.Radius * circle.Radius * Math.PI; } } return area; } } public class Square { public double Height { get; set; } } public class Circle { public double Radius { get; set; } } public class Triangle { public double FirstSide { get; set; } public double SecondSide { get; set; } public double ThirdSide { get; set; } }
در اینجا کلاس AreaCalculator برای محاسبه مساحت تمام اشیاء ورودی، مساحت تک تک اشیاء را محاسبه میکند و نتیجه را برمیگرداند. در این مثال با اضافه شدن شکل هندسی جدید، باید کد این کلاس تغییر کند که با اصل Open/Closed مغایر است. برای بهبود این کد طراحی زیر پیشنهاد شده است:
public class AreaCalculator { public double Area(Shape[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Area(); } return area; } } public abstract class Shape { public abstract double Area(); } public class Square : Shape { public double Height { get { return _height; } } private double _height; public Square(double Height) { _height = Height; } public override double Area() { return Math.Sqrt(_height); } } public class Circle : Shape { public double Radius { get { return _radius; } } private double _radius; public Circle(double Radius) { _radius = Radius; } public override double Area() { return _radius * _radius * Math.PI; } } public class Triangle : Shape { public double FirstSide { get { return _firstSide; } } public double SecondSide { get { return _secondSide; } } public double ThirdSide { get { return _thirdSide; } } private double _firstSide; private double _secondSide; private double _thirdSide; public Triangle(double FirstSide, double SecondSide, double ThirdSide) { _firstSide = FirstSide; _secondSide = SecondSide; _thirdSide = ThirdSide; } public override double Area() { double TotalHalf = (_firstSide + _secondSide + _thirdSide) / 2; return Math.Sqrt(TotalHalf * (TotalHalf - _firstSide) * (TotalHalf - _secondSide) * (TotalHalf - _thirdSide)); } }
در این طراحی، پیچیدگی محاسبه مساحت هر شکل به کلاس آن شکل منتقل شده است و با اضافه شدن شکل جدید نیازی به تغییر کلاس AreaCalculator نداریم.
در مقالهی بعدی به سه اصل دیگر اصول SOLID خواهم پرداخت.
public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; }
partial class CarModel : INotifyPropertyChanged { private double _speedKmPerHour; public double SpeedKmPerHour { get => _speedKmPerHour; set { _speedKmPerHour = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SpeedKmPerHour))); } } public event PropertyChangedEventHandler? PropertyChanged; }
partial class CarModel : INotifyPropertyChanged { private double _speedKmPerHour; }
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" /> </ItemGroup> </Project>
[Generator] public class NotifyPropertyChangedGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) {
public void Execute(GeneratorExecutionContext context) { // uncomment to debug the actual build of the target project // Debugger.Launch(); var compilation = context.Compilation; var notifyInterface = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged"); foreach (var syntaxTree in compilation.SyntaxTrees) { var semanticModel = compilation.GetSemanticModel(syntaxTree); var immutableHashSet = syntaxTree.GetRoot() .DescendantNodesAndSelf() .OfType<ClassDeclarationSyntax>() .Select(x => semanticModel.GetDeclaredSymbol(x)) .OfType<ITypeSymbol>() .Where(x => x.Interfaces.Contains(notifyInterface)) .ToImmutableHashSet(); foreach (var typeSymbol in immutableHashSet) { var source = GeneratePropertyChanged(typeSymbol); context.AddSource($"{typeSymbol.Name}.Notify.cs", source); } } }
private string GeneratePropertyChanged(ITypeSymbol typeSymbol) { return $@" using System.ComponentModel; namespace {typeSymbol.ContainingNamespace} {{ partial class {typeSymbol.Name} {{ {GenerateProperties(typeSymbol)} public event PropertyChangedEventHandler? PropertyChanged; }} }}"; } private static string GenerateProperties(ITypeSymbol typeSymbol) { var sb = new StringBuilder(); var suffix = "BackingField"; foreach (var fieldSymbol in typeSymbol.GetMembers().OfType<IFieldSymbol>() .Where(x=>x.Name.EndsWith(suffix))) { var propertyName = fieldSymbol.Name[..^suffix.Length]; sb.AppendLine($@" public {fieldSymbol.Type} {propertyName} {{ get => {fieldSymbol.Name}; set {{ {fieldSymbol.Name} = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof({propertyName}))); }} }}"); } return sb.ToString(); }
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <ProjectReference Include="..\NotifyPropertyChangedGenerator\NotifyPropertyChangedGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> </ItemGroup> </Project>
using System.ComponentModel; namespace NotifyPropertyChangedGenerator.Demo { public partial class CarModel : INotifyPropertyChanged { private double SpeedKmPerHourBackingField; private int NumberOfDoorsBackingField; private string ModelBackingField = ""; public void SpeedUp() => SpeedKmPerHour *= 1.1; } }
public DateTime deliveryDate { get; set; }
@Html.TextBoxFor(model => model.deliveryDate, new { @id = "TextBox1",onclick = "PersianDatePicker.Show(this,'PersianDateTime.Now.now.ToString(PersianDateTimeFormat.Date)')" })
public type this [type identifier] { get{ ... } set{ ... } }
public class Matrix { // فیلدها private int _row, _col; private readonly double[,] _values; // تعداد ردیفهای ماتریس public int Row { get { return _row; } set { _row = value > 0 ? value : 3; } } // تعداد ستونهای ماتریس public int Col { get { return _col; } set { _col = value > 0 ? value : 3; } } // نعریف یک ایندکسر public double this[int r, int c] { get { return Math.Round(_values[r, c], 3); } set { _values[r, c] = value; } } // سازنده 1 public Matrix() { _values = new double[_row,_col]; } // سازنده 2 public Matrix(int row, int col) { _row = row; _col = col; _values = new double[_row,_col]; } }
public class UseMatrixIndexer { // ایجاد نمونه از شیء ماتریس private readonly Matrix m = new Matrix(5, 5); private double item; public UseMatrixIndexer() { // دسترسی به عنصر واقع در ردیف چهار و ستون سه item = m[4, 3]; } }
public static void Initialize(Assembly assembly) { //register global convertors. AutoMapper.Mapper.CreateMap<DateTime, string>().ConvertUsing<DateTimeToPersianDateTimeConverter>(); var typesToMap = from t in assembly.GetTypes() let attr = t.GetCustomAttribute<MapFromAttribute>() where attr != null select new {SourceType = attr.SourceType, Destination = t, Attribute = attr}; foreach (var map in typesToMap) { AutoMapper.Mapper.CreateMap(map.SourceType, map.Destination) .DoMapForMemberAttribute() // for different property names in source and destination .DoIgnoreMapAttribute()// ignore specified properties .DoUseValueResolverAttribute()// set value resolvers .DoIgnoreAllNonExisting()// its have to be the latest. ; } //endeach AutoMapper.Mapper.AssertConfigurationIsValid(); }
public static IMappingExpression DoMapForMemberAttribute(this IMappingExpression expression) { var ok = from p in expression.TypeMap.DestinationType.GetProperties() let attr = p.GetCustomAttribute<MapForMemberAttribute>() where attr != null select new {AttributeValue = attr, PropertyName = p.Name}; foreach (var property in ok) { expression.ForMember(property.PropertyName, opt => opt.MapFrom(property.AttributeValue.MemberToMap)); } return expression; }
public static IMappingExpression DoIgnoreAttribute(this IMappingExpression expression) { foreach (var property in expression.TypeMap.DestinationType.GetProperties() .Where(x => x.GetCustomAttribute<IgnoreMapAttribute>() != null)) { expression.ForMember(property.Name, opt => opt.Ignore()); } return expression; }
public static IMappingExpression DoUseValueResolverAttribute(this IMappingExpression expression) { var ok = from p in expression.TypeMap.DestinationType.GetProperties() let attr = p.GetCustomAttribute<UseValueResolverAttribute>() where attr != null select new {AttributeValue = attr, PropertyName = p.Name}; foreach (var property in ok) { expression.ForMember(property.PropertyName, opt => opt.ResolveUsing(property.AttributeValue.ValueResolver)); } return expression; }
public static IMappingExpression DoIgnoreAllNonExisting(this IMappingExpression expression) { var attr = expression.TypeMap.DestinationType.GetCustomAttribute<MapFromAttribute>(); if (attr?.IgnoreAllNonExistingProperty == false)//instead of if(attr == null || attr.IgnoreAllNonExistingProperty == false) return expression; foreach (var property in expression.TypeMap.GetUnmappedPropertyNames()) { expression.ForMember(property, opt => opt.Ignore()); } return expression; }
public class Student { public virtual int Id { set; get; } public virtual string Name { set; get; } public virtual string Family { set; get; } public virtual string Email { set; get; } public virtual DateTime RegisterDateTime { set; get; } public virtual ICollection<Book> Books { set; get; } } public class Book { public virtual int Id { set; get; } public virtual string Name { set; get; } public virtual DateTime BorrowDateTime { set; get; } public virtual DateTime ExpiredDateTime { set; get; } public virtual decimal Price { set; get; } [ForeignKey("StudentIdFk")] public virtual Student Student { set; get; } public virtual int StudentIdFk { set; get; } }
[MapFrom(typeof (Student), ignoreAllNonExistingProperty: true, alsoCopyMetadata: true)] public class AdminStudentViewModel { // [IgnoreMap] public int Id { set; get; } [MapForMember("Name")] public string FirstName { set; get; } [MapForMember("Family")] public string LastName { set; get; }
[IgnoreMap] public string Email { set; get; } [MapForMember("RegisterDateTime")] public string RegisterDateTimePersian { set; get; } [UseValueResolver(typeof (BookCountValueResolver))] public int BookCounts { set; get; } [UseValueResolver(typeof (BookPriceValueResolver))] public decimal TotalBookPrice { set; get; } };
public class BookCountValueResolver : ValueResolver<Student, int> { protected override int ResolveCore(Student source) => source.Books.Count; }; public class BookPriceValueResolver : ValueResolver<Student, decimal> { protected override decimal ResolveCore(Student source) => source.Books.Sum(b => b.Price); };
static void Main(string[] args) { var assemblyToLoad = Assembly.GetAssembly(typeof (AdminStudentViewModel));//get assembly global::AttributesForAutomapper.Configuration.Initialize(assemblyToLoad);//init automaper IList<Student> lst; using (var context = new MySampleContext()) { lst = context.Students.Include(x => x.Books).ToList(); } foreach (var student in lst) { WriteLine( $"[{student.Id}]*\n{student.Name} {student.Family}.\nmailto:{student.Email}.\nRegistered at'{student.RegisterDateTime}'"); foreach (var book in student.Books) WriteLine($"\tBook name:{book.Name}, Book price:{book.Price}"); } var lstViewModel = AutoMapper.Mapper.Map<IList<Student>, IList<AdminStudentViewModel>>(lst); foreach (var adminStudentViewModel in lstViewModel) { WriteLine( $"[{adminStudentViewModel.Id}]*\n\t{adminStudentViewModel.FirstName} {adminStudentViewModel.LastName}.\n\t" + $"mailto:{adminStudentViewModel.Email}.\n\tRegistered at'{adminStudentViewModel.RegisterDateTimePersian}'\n\t" + $"Book Counts: {adminStudentViewModel.BookCounts} with total price of {adminStudentViewModel.TotalBookPrice}"); } WriteLine("Press any key to exit..."); ReadKey(); }
[1]* Morteza Raeisi. mailto:MrRaeisi@outlook.com. Registered at'23/08/1392 19:11:43' // I'm using Windows 10 with Persian calendar as default, On other OS or calendar settings, this value is different. Book name:AutoMapper Attr, Book price:1000.00 Book name:Second Book, Book price:2500.00 Book name:Hungry Book, Book price:2500.00 ... [1]* Morteza Raeisi. //MapForMemebers mailto:. // IgnoreMap Registered at'1392/08/23 19:11' // Convert using Book Counts: 3 with total price of 6000.00 // Value resolvers ...
public class Person { private string _name; public string Name { get { return _name; } } public Person(string name) { _name = name; } }
using System; using System.Reflection; using System.Reflection.Emit; namespace FastReflectionTests { class Program { static void Main(string[] args) { //اسمبلی محل قرارگیری کدهای پویای نهایی در اینجا تعیین میشود //حالت دسترسی به آن اجرایی درنظر گرفته شده، امکان تعیین حالتهای دیگری مانند ذخیره سازی نیز وجود دارد var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( name: new AssemblyName("Demo"), access: AssemblyBuilderAccess.Run); // اکنون داخل این اسمبلی یک ماژول جدید را برای قرار دادن کلاس جدید خود تعریف میکنیم var moduleBuilder = assemblyBuilder.DefineDynamicModule(name: "PersonModule"); // کار ساخت نوع و کلاس جدید شخص عمومی از اینجا شروع میشود var typeBuilder = moduleBuilder.DefineType(name: "Person", attr: TypeAttributes.Public); // افزودن فیلد خصوصی نام تعریف شده در سطح کلاس شخص var nameField = typeBuilder.DefineField(fieldName: "_name", type: typeof(string), attributes: FieldAttributes.Private); // تعریف سازنده عمومی کلاس شخص که دارای یک آرگومان رشتهای است var ctor = typeBuilder.DefineConstructor( attributes: MethodAttributes.Public, callingConvention: CallingConventions.Standard, parameterTypes: new[] { typeof(string) }); // تعریف بدنه سازنده کلاس شخص // در اینجا فیلد خصوصی تعریف شده در سطح کلاس باید مقدار دهی شود var ctorIL = ctor.GetILGenerator(); // نکتهای در مورد سازندهها ctorIL.Emit(OpCodes.Ldarg_0); // اندیس صفر در سازنده کلاس به وهلهای از کلاس جاری اشاره میکند ctorIL.Emit(OpCodes.Ldarg_1); // بارگذاری آرگومان سازنده و قرار دادن آن روی پشته // مقدار دهی فیلد خصوصی نام که به وهلهای از کلاس جاری و مقدار آرگومان دریافتی نیاز دارد ctorIL.Emit(OpCodes.Stfld, nameField); ctorIL.Emit(OpCodes.Ret); // پایان کار سازنده // تعریف خاصیت رشتهای نام در کلاس شخص var nameProperty = typeBuilder.DefineProperty( name: "Name", attributes: PropertyAttributes.HasDefault, returnType: typeof(string), parameterTypes: null); // خاصیت پارامتر ورودی ندارد var namePropertyGetMethod = typeBuilder.DefineMethod( name: "get_Name", attributes: MethodAttributes.Public | //متد ویژهای است که توسط کامپایلر پردازش و تشخیص داده میشود MethodAttributes.SpecialName | MethodAttributes.HideBySig, returnType: typeof(string), parameterTypes: Type.EmptyTypes); // اتصال گت متد به خاصیت رشتهای نام که پیشتر تعریف شد nameProperty.SetGetMethod(namePropertyGetMethod); // بدنه گت متد در اینجا تعریف خواهد شد var namePropertyGetMethodIL = namePropertyGetMethod.GetILGenerator(); namePropertyGetMethodIL.Emit(OpCodes.Ldarg_0); // بارگذاری اشارهگری به وهلهای از کلاس جاری در پشته namePropertyGetMethodIL.Emit(OpCodes.Ldfld, nameField); // بارگذاری فیلد نام namePropertyGetMethodIL.Emit(OpCodes.Ret); var t = typeBuilder.CreateType(); // نهایی سازی کار ایجاد نوع جدید // ایجاد وهلهای از نوع جدید که پارامتری رشتهای به سازنده آن ارسال میشود var instance = Activator.CreateInstance(t, "Vahid"); // دسترسی به خاصیت نام var nProperty = t.GetProperty("Name"); // و دریافت مقدار آن برای نمایش var result = nProperty.GetValue(instance, null); Console.WriteLine(result); } } }