var table = new PdfPTable(numColumns: 3) { WidthPercentage = 100, RunDirection = PdfWriter.RUN_DIRECTION_RTL, ExtendLastRow = false, }; Image i = Image.GetInstance(_imagePath); //logo table.AddCell(new PdfPCell(i) { HorizontalAlignment = Element.ALIGN_LEFT, Border = 0 }); //title table.AddCell(new PdfPCell(new Phrase("باسمه تعالی " + "\r\n" + "حوزه علمیه امیر المومنین (ع) - معاونت آموزش - کارنامه تحصیلی طلبه", font)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_CENTER, Border = 0, } ); //date table.AddCell(new PdfPCell(new Phrase(DateTime.Now.Date.ToString(), font)) { HorizontalAlignment = 2, Border = 0 }); int[] topTableColumnsWidth = { 5, 35, 5 }; table.SetWidths(topTableColumnsWidth); return table.AddBorderToTable();
1,Ahmad,Mohammadi 2,Farhad,Farahmandkhah 3,Amin,Esapor 4,Reza,shayesteh 5,Maryam,Ebrahimi 6,Farnaz,Akrami
Create Table Testloader (ID int, FirstName varchar(255), LastName Varchar(255))
LOAD DATA INFILE 'c:\LoaderTable.txt' Insert INTO TABLE Testloader FIELDS TERMINATED BY ',' optionally enclosed by '"' TRAILING NULLCOLS ( ID, FirstName, LastName )
D:\>sqlldr userid=Username/Password@Servicename data='c:\LoaderTable.txt' control='c:\loader.ctl' log='c:\log.txt' bad='c:\logbad.bad'
- یک اپلیکیشن جدید ASP.NET MVC با تنظیمات Individual User Accounts بسازید.
- احراز هویت فیسبوک را توسط کلید هایی که از Facebook دریافت کرده اید فعال کنید. برای اطلاعات بیشتر در این باره میتوانید به این لینک مراجعه کنید.
- برای درخواست اطلاعات بیشتر از فیسبوک، فایل Startup.Auth.cs را مطابق لیست زیر ویرایش کنید.
List<string> scope = newList<string>() { "email", "user_about_me", "user_hometown", "friends_about_me", "friends_photos" }; var x = newFacebookAuthenticationOptions(); x.Scope.Add("email"); x.Scope.Add("friends_about_me"); x.Scope.Add("friends_photos"); x.AppId = "636919159681109"; x.AppSecret = "f3c16511fe95e854cf5885c10f83f26f"; x.Provider = newFacebookAuthenticationProvider() { OnAuthenticated = async context => { //Get the access token from FB and store it in the database and //use FacebookC# SDK to get more information about the user context.Identity.AddClaim( new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken)); } }; x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie; app.UseFacebookAuthentication(x);
<li> @Html.ActionLink("FacebookInfo", "FacebookInfo","Account") </li>
// // GET: /Account/LinkLoginCallback publicasyncTask<ActionResult> LinkLoginCallback() { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); if (loginInfo == null) { return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login); if (result.Succeeded) { var currentUser = await UserManager.FindByIdAsync(User.Identity.GetUserId()); //Add the Facebook Claim await StoreFacebookAuthToken(currentUser); return RedirectToAction("Manage"); } return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); }
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) { if (User.Identity.IsAuthenticated) { return RedirectToAction("Manage"); } if (ModelState.IsValid) { // Get the information about the user from the external login provider var info = await AuthenticationManager.GetExternalLoginInfoAsync(); if (info == null) { return View("ExternalLoginFailure"); } var user = newApplicationUser() { UserName = model.Email }; var result = await UserManager.CreateAsync(user); if (result.Succeeded) { result = await UserManager.AddLoginAsync(user.Id, info.Login); if (result.Succeeded) { await StoreFacebookAuthToken(user); await SignInAsync(user, isPersistent: false); return RedirectToLocal(returnUrl); } } AddErrors(result); } ViewBag.ReturnUrl = returnUrl; return View(model); }
// // GET: /Account/ExternalLoginCallback [AllowAnonymous] publicasyncTask<ActionResult> ExternalLoginCallback(string returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } // Sign in the user with this external login provider if the user already has a login var user = await UserManager.FindAsync(loginInfo.Login); if (user != null) { //Save the FacebookToken in the database if not already there await StoreFacebookAuthToken(user); await SignInAsync(user, isPersistent: false); return RedirectToLocal(returnUrl); } else { // If the user does not have an account, then prompt the user to create an account ViewBag.ReturnUrl = returnUrl; ViewBag.LoginProvider = loginInfo.Login.LoginProvider; return View("ExternalLoginConfirmation", newExternalLoginConfirmationViewModel { Email = loginInfo.Email }); } }
privateasyncTask StoreFacebookAuthToken(ApplicationUser user) { var claimsIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie); if (claimsIdentity != null) { // Retrieve the existing claims for the user and add the FacebookAccessTokenClaim var currentClaims = await UserManager.GetClaimsAsync(user.Id); var facebookAccessToken = claimsIdentity.FindAll("FacebookAccessToken").First(); if (currentClaims.Count() <=0 ) { await UserManager.AddClaimAsync(user.Id, facebookAccessToken); }
public class FacebookViewModel { [Required] [Display(Name = "Friend's name")] public string Name { get; set; } public string ImageURL { get; set; } }
//GET: Account/FacebookInfo [Authorize] publicasyncTask<ActionResult> FacebookInfo() { var claimsforUser = await UserManager.GetClaimsAsync(User.Identity.GetUserId()); var access_token = claimsforUser.FirstOrDefault(x => x.Type == "FacebookAccessToken").Value; var fb = newFacebookClient(access_token); dynamic myInfo = fb.Get("/me/friends"); var friendsList = newList<FacebookViewModel>(); foreach (dynamic friend in myInfo.data) { friendsList.Add(newFacebookViewModel() { Name = friend.name, ImageURL = @"https://graph.facebook.com/" + friend.id + "/picture?type=large" }); } return View(friendsList); }
@model IList<WebApplication96.Models.FacebookViewModel> @if (Model.Count > 0) { <h3>List of friends</h3> <div class="row"> @foreach (var friend in Model) { <div class="col-md-3"> <a href="#" class="thumbnail"> <img src=@friend.ImageURL alt=@friend.Name /> </a> </div> } </div> }
این یک مثال ساده از کار کردن با تامین کنندگان اجتماعی بود. همانطور که مشاهده میکنید، براحتی میتوانید دادههای بیشتری برای کاربر جاری درخواست کنید و تجربه کاربری و امکانات بسیار بهتری را در اپلیکیشن خود فراهم کنید.
مستند سازی Database
dbdescis a powerful tool to help you document your databases. It can produce detailed documents describing your databases
Currently dbdesc supports the following databases:
SQL Server 2000, 2005, 2008, 2008 R2, 2012, 2014
Microsoft Desktop Engine 2000 (MSDE) and SQL Server Express editions
MySQL 5.0
Oracle 9 and above
Microsoft Access 97 and above
Firebird
با استفاده از کوئری زیر نیز میتوانید نام و Description ستونها و دیتاتایپ و ... آنها را بدست آورید.
SELECT p.name ,p.value ,t.name AS TableName ,c.name AS ColumnName ,c.is_nullable ,c.max_length ,TYPE_NAME(c.system_type_id) FROM sys.tables t JOIN sys.columns c ON t.object_id = c.object_id LEFT JOIN sys.extended_properties p ON p.major_id = t.object_id AND p.minor_id = c.column_id ORDER BY t.name DESC,c.name DESC
جهت بررسی بیشتر و آشنایی با Extended Properties in SQL Server به لینک Towards the Self-Documenting SQL Server Database مراجعه کنید.
اگر برنامهی شما برای مثال با SMO مربوط به اس کیوال سرور 2008 کامپایل شود، روی سروری با SQL Server 2005 کار نخواهد کرد و پیغام میدهد که نگارش 10 اسمبلی Microsoft.SqlServer.Management.Sdk.Sfc یافت نشد.
یک راه حل آن، نصب Microsoft SQL Server 2008 Management Objects بر روی سرور است، یا راه حل دوم، پیدا کردن اسمبلیهایی که برنامه به آنها ارجاع دارد و کپی کردن آنها کنار فایل اجرایی برنامه در سرور. (درست کردن یک برنامه پرتابل دات نتی، یا نسبتا پرتابل!)
برای این منظور کلاس زیر تهیه شده است که مسیر فایل اجرایی یا dll یک پروژه را دریافت کرده و لیست تمام ارجاعات به آنرا به صورت بازگشتی پیدا میکند. (البته در قسمت یافتن مسیر اسمبلیها، اسمبلیهای سیستمی که با خود دات نت فریم ورک نصب میشوند، حذف شده است)
using System.Collections.Generic;
using System.Reflection;
using System.IO;
namespace App
{
class CFindRef
{
#region Fields (2)
/// <summary>
/// لیستی جهت نگهداری نام اسمبلیها
/// </summary>
private readonly List<string> _assemblies = new List<string>();
/// <summary>
/// لیستی جهت نگهداری مسیر اسمبلیها
/// </summary>
private readonly List<string> _filePath = new List<string>();
#endregion Fields
#region Constructors (1)
/// <summary>
/// سازنده کلاس
/// </summary>
/// <param name="fileName">مسیر اولیه اسمبلی مورد نظر</param>
public CFindRef(string fileName)
{
ListReferences(fileName);
}
#endregion Constructors
#region Properties (2)
/// <summary>
/// لیست مسیر اسمبلیهایی که به آنها ارجاعی وجود دارد منهای موارد سیستمی
/// </summary>
public List<string> ReferencedFiles
{
get
{
_filePath.Sort();
return _filePath;
}
}
/// <summary>
/// لیست کامل اسمبلیهایی که اسمبلی ما به آنها وابسته است
/// </summary>
public List<string> ReferencedNames
{
get
{
_assemblies.Sort();
return _assemblies;
}
}
#endregion Properties
#region Methods (1)
// Private Methods (1)
/// <summary>
/// متدی بازگشتی جهت یافتن لیست ارجاعات
/// </summary>
/// <param name="path">مسیر یا نام اسمبلی</param>
private void ListReferences(string path)
{
//آیا تکراری است؟
if (_assemblies.Contains(path))
return;
Assembly asm;
// آیا فایل است یا نام کامل اسمبلی
if (File.Exists(path))
{
// load the assembly from a path
asm = Assembly.LoadFrom(path);
}
else
{
// سعی در بارگذاری اسمبلی
try
{
asm = Assembly.Load(path);
}
catch
{
asm = null; //جای بهبود دارد
}
}
if (asm == null) return;
// افزودن به لیست نامها
_assemblies.Add(path);
string asmLocation = asm.Location;
//حذف موارد سیستمی از لیست مسیر فایلها
if (asmLocation != null && !asmLocation.Contains("\\System.")
&& !asmLocation.Contains("\\mscorlib"))
_filePath.Add(asmLocation);
// پیدا کردن ارجاعها
AssemblyName[] imports = asm.GetReferencedAssemblies();
// iterate
foreach (AssemblyName asmName in imports)
{
// فراخوانی بازگشتی جهت یافتن تمامی ارجاعات
ListReferences(asmName.FullName);
}
}
#endregion Methods
}
}
مثالی در مورد نحوهی استفاده از آن:
CFindRef cfr = new CFindRef(@"C:\App\test.exe");
foreach (var asmRef in cfr.ReferencedFiles)
{
textBox1.Text += asmRef + Environment.NewLine;
//Application.DoEvents();
}
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.ConnectionInfo\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ConnectionInfo.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Dmf\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Dmf.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Management.Sdk.Sfc\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Management.Sdk.Sfc.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.ServiceBrokerEnum\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ServiceBrokerEnum.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Smo\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Smo.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.SqlClrProvider\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.SqlClrProvider.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.SqlEnum\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.SqlEnum.dll
برنامه آماده هم در این زمینه موجود است، برای مثال CheckAsm
مشاهده سایت
public class UserCanSeeProjectRequirement : IAuthorizationRequirement { public UserCanSeeProjectRequirement() { } } public class UserCanSeeProjectHandler : AuthorizationHandler<UserCanSeeProjectRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserCanSeeProjectRequirement requirement) { //claim-based validation if (context.User.HasClaim("permission.cansee", "CanSee")) context.Succeed(requirement); //role-based validation if (context.User.IsInRole("admin") || context.User.IsInRole("user")) context.Succeed(requirement); return Task.CompletedTask; } }
namespace BlazorWasm.Client { public class Program { public static async Task Main(string[] args) { // ... services.AddScoped<IAuthorizationHandler, UserCanSeeProjectHandler>(); services.AddAuthorizationCore(options => { options.AddPolicy("UserCanSeeProjectPolicy", policy => policy.Requirements.Add(new UserCanSeeProjectRequirement())); }); // ... } } }
@attribute [Authorize(Policy = "UserCanSeeProjectPolicy")]
<AuthorizeView Policy="UserCanSeeProjectPolicy"> <NotAuthorized> <h2 class="mt-5">You are not authorized to view this page</h2> </NotAuthorized> <Authorized> <div class="container my-profile"> --- Place here all the content you want your user to view ---- </div> </Authorized> </AuthorizeView>
- نحوه ارسال فایل به سرور توسط ASP.NET MVC
- نحوه اعتبار سنجی سمت سرور ارسال فایلها
در ASP.NET MVC برای آپلود فایلها عموما عنوان میشود که از تگ input به نحو زیر استفاده شود:
<input type="file" name="file" />
<input type="file" data-val="true" data-val-required="لطفا فایلی را انتخاب کنید" name="file" /> @Html.ValidationMessage("file")
از این نکته خصوصا در طراحی html helperهای سفارشی نیز میتوان استفاده کرد.
برای مثال فرض کنید مشغول طراحی یک کنترل captcha هستید. قسمتی از آن مربوط به دریافت ورودی از کاربر است. به علاوه، پیغام خطایی هم که باید نمایش داده شود نیز باید توسط کاربر قابلیت سفارشی شدن را داشته باشد (و نمیتوان آنرا توسط یک attribute به سادگی به یک مدل خاص انتساب داد).
در این حالت تنظیم data-val و data-val-required به کمک anonymously typed objects پارامتر htmlAttributes میسر نیست (چون بین حروف آن dash وجود دارد) و باید از overload و نوع dictionary دار آن به نحو زیر استفاده کرد:
htmlHelper.TextBox("CaptchaInputText", string.Empty, new Dictionary<string, object> { { "data-val", "true"}, { "data-val-required", validationErrorMessage}, })
Primary Constructors چیست؟
Primary Constructors، قابلیتی است که به C# 12 اضافه شدهاست تا توسط آن بتوان خواص را مستقیما توسط پارامترهای سازندهی یک کلاس تعریف و همچنین مقدار دهی کرد. هدف از آن، کاهش قابل ملاحظهی یکسری کدهای تکراری و مشخص است تا به کلاسهایی زیباتر، کمحجمتر و خواناتر برسیم. برای مثال کلاس متداول زیر را درنظر بگیرید:
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; } }
اکنون اگر بخواهیم همین کلاس را با استفاده از ویژگی Primary Constructor اضافه شده به C# 12.0 بازنویسی کنیم، به قطعه کد زیر میرسیم:
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);
یک نکته: اگر از Rider و یا ReSharper استفاده میکنید، یک چنین Refactoring توکاری جهت سهولت کار، به آنها اضافه شدهاست و به سرعت میتوان این تبدیلات را توسط آنها انجام داد.
توضیحات:
- متد سازنده در این حالت، به ظاهر حذف شده و به قسمت تعریف کلاس منتقل شدهاست.
- تمام مقدار دهیهای آغازین موجود در متد سازندهی پیشین نیز حذف شدهاند و مستقیما به قسمت تعریف خواص، منتقل شدهاند.
در نتیجه از یک کلاس 15 سطری، به کلاسی 7 سطری رسیدهایم که کاهش حجم قابل ملاحظهای را پیدا کردهاست.
نکته 1: هیچ ضرورتی وجود ندارد که به همراه یک primary constructor، خواصی هم مانند مثال فوق ارائه شوند؛ چون پارامترهای آن در تمام اعضای این کلاس، به همین شکل، قابل دسترسی هستند. در این مثال صرفا جهت بازسازی کد قبلی، این خواص اضافی را مشاهده میکنید. یعنی اگر تنها قرار بود، کار تزریق وابستگیها صورت گیرد که عموما به همراه تعریف فیلدهایی جهت انتساب پارامترهای متد سازنده به آنها است، استفاده از یک primary constructor، کدهای فوق را بیش از این هم فشردهتر میکرد و ... یک سطری میشد.
نکته 2: استفاده از پارامترهای سازندهی اولیه، صرفا جهت مقدار دهی خواص عمومی یک کلاس، یک code smell هستند! چون میتوان یک چنین کارهایی را به نحو شکیلتری توسط required properties معرفی شدهی در C# 11، پیاده سازی کرد.
بررسی تاریخچهی primary constructors
همانطور که در مقدمهی بحث نیز عنوان شد، primary constructors قابلیت جدیدی نیست و برای نمونه به همراه C# 9 و مفهوم جدید رکوردهای آن، ارائه شد:
public record class Book(string Title, string Publisher);
پس از آن در C# 10، این توسعه ادامه یافت و به امکان تعریف record structها، بسط یافت که در اینجا هم قابلیت تعریف primary constructors وجود دارد:
public record struct Color(int R, int G, int B);
پس از این مقدمات، اکنون در C# 12 نیز میتوان primary constructors را به تمام کلاسها و structهای معمولی هم اعمال کرد؛ با این تفاوت که در اینجا برخلاف رکوردها، کدهای خواصهای متناظر، به صورت خودکار تولید نمیشوند و اگر به آنها نیاز دارید، باید آنها را همانند مثال ابتدای بحث، خودتان به صورت دستی تعریف کنید.
primary constructors کلاسها و structهای معمولی، با primary constructors رکوردها یکی نیست
در C# 12 و به همراه معرفی primary constructors مخصوص کلاسها و structهای معمولی آن، از روش متفاوتی برای دسترسی به پارامترهای تعریف شده، استفاده میکند که به آن capturing semantics هم میگویند. در این حالت پارامترهای تعریف شدهی در یک primary constructor، توسط هر عضوی از آن کلاس قابل استفادهاست که یکی از کاربردهای آن، ساده کردن تعاریف تزریق وابستگیها است. در این حالت دیگر نیازی نیست تا ابتدا یک فیلد را برای انتساب به پارامتر تزریق شده تعریف کرد و سپس از آن فیلد، استفاده نمود؛ مستقیما میتوان با همان پارامتر تعریف شده، در متدها و اعضای کلاس، کار کرد.
برای مثال سرویس زیر را که از تزریق وابستگیها، در سازندهی خود استفاده میکند، درنظر بگیرید:
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(); } }
البته مفهوم Captures هم در زبان #C جدید نیست و در ابتدا به همراه anonymous methods و بعدها به همراه lambda expressions، معرفی و بکار گرفته شد. برای مثال درون یک lambda expression، اگر از متغیری خارج از آن lambda expressions استفاده شود، کامپایلر یک capture از آن متغیر را تهیه کرده و استفاده میکند.
بنابراین به صورت خلاصه primary constructors در رکوردها، با هدف تعریف خواص عمومی فقط خواندنی، ارائه شدند؛ اما primary constructors ارائه شدهی در C# 12 که اینبار قابل اعمال به کلاسها و structs معمولی است، بیشتر هدف ساده سازی تعریف کدهای تکراری private fields را دنبال میکند. برای نمونه این کدی است که کامپایلر برای primary constructor مثال ابتدای بحث تولید میکند و در اینجا نحوهی تولید خودکار این فیلدهای خصوصی را مشاهده میکنید:
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; } } } }
یک نکته: برای مشاهدهی یک چنین کدهایی میتوانید از منوی Tools->IL Viewer برنامهی Rider استفاده کرده و در برگهی ظاهر شده، گزینهی #Low-Level C آنرا انتخاب نمائید.
امکان تعریف سازندههای دیگر، به همراه سازندهی اولیه
اگر به کدهای #Low-Level C تولیدی فوق دقت کنید، این کلاس، به همراه یک سازندهی خالی بدون پارامتر (parameter less constructor) نیست و سازندهی پیشفرضی (default constructor) برای آن درنظر گرفته نشدهاست ... اما اگر کلاسی به همراه یک primary constructor تعریف شد، میتوان با استفاده از واژهی کلیدی this، سازندهی ثانویهای را هم برای آن تعریف کرد:
public class Person(string firstName, string lastName) { public Person() : this("John", "Smith") { } public Person(string firstName) : this(firstName, "Smith") { } public string FullName => $"{firstName} {lastName}"; }
امکان ارثبری و تعریف سازندهی اولیه
مثال زیر را درنظر بگیرید که در آن کلاس مشتق شدهی از کلاس User، یک سازندهی اولیه را تعریف کرده:
public class User { public User(string firstName, string lastName) { } } public class Editor(string firstName, string lastName) : User { }
البته این محدودیت با structها وجود ندارد؛ چون structها، value type هستند و همواره به صورت پیشفرض، به همراه یک سازندهی پیش فرض بدون پارامتر، تولید میشوند.
یک مثال: قطعه کد متداول ارثبری زیر را درنظر بگیرید که در آن، کلاس مشتق شده به کمک واژهی کلید base، امکان تعریف سازندهی جدیدی را یافته و یکی از پارامترهای سازندهی کلاس پایه را مقدار دهی میکند:
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);
و یا یک نمونه مثال دیگر آن به صورت زیر است که در آن، ذکر بدنهی کلاس در C# 12، الزامی ندارد:
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(); }
توصیه به پرهیز از double capturing
با مفهوم capture در این مطلب آشنا شدیم. در مثال زیر دوبار از پارامتر سازندهی age، در دو قسمت عمومی شده، استفاده شدهاست:
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! // !
درک بهتر آن، نیاز به #Low-Level C کلاس Human را دارد:
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(); }
[DllImport( "nativelib", EntryPoint = "to_lower", CharSet = CharSet.Unicode)] internal static extern string ToLower(string str); // string lower = ToLower("StringToConvert");
تولید کنندهی کد مخصوص P/Invoke در دات نت 7، به دنبال ویژگی جدید LibraryImportAttribute بر روی متدهای استاتیک و partial میگردد تا کدهای متناظر با آنها را تولید کند. به این ترتیب نیاز به تولید اینگونه کدها در زمان اجرای برنامه مرتفع میشود و همچنین میتوان این کدها را در IDE خود بررسی و حتی دیباگ کرد.
[LibraryImport( "nativelib", EntryPoint = "to_lower", StringMarshalling = StringMarshalling.Utf16)] internal static partial string ToLower(string str);
امکان تبدیل خودکار کدهای قدیمی مبتنی بر DllImportAttribute به نمونههای جدید
برای تبدیل خودکار کدهای قدیمی موجود، فقط کافی است یک سطر زیر را به فایل editorconfig. پروژهی خود اضافه کنید:
dotnet_diagnostic.SYSLIB1054.severity = suggestion
تغییرات صورت گرفته نسبت به DllImport قدیمی
نحوهی تعریف LibraryImportAttribute در اکثر موارد با DllImportAttribute تطابق دارد، منهای موارد زیر:
- در اینجا معادلی برای CallingConvention وجود ندارد. برای اینکار از UnmanagedCallConvAttribute استفاده میشود.
- CharSet با StringMarshalling تعویض شدهاست. ANSI حذف شدهاست و UTF-8 حالت پیشفرض است. برای مثال:
// Before public static class Native { [DllImport(nameof(Native), CharSet = CharSet.Unicode)] public extern static string ToLower(string str); } // After public static partial class Native { [LibraryImport(nameof(Native), StringMarshalling = StringMarshalling.Utf16)] public static partial string ToLower(string str); }