مطالب
معرفی Selector های CSS - قسمت 4
31- :disabled
تگ‌هایی را انتخاب می‌نماید که دارای ویژگی disabled می‌باشند.
<style>
    :disabled {
        background: yellow;
    }
</style>
<input disabled="disabled"/>
<input/>
<input disabled="disabled"/>
<input />
در مثال فوق input اول و سوم به رنگ زرد نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :disabled  3

32- :enabled
تگ‌هایی را انتخاب می‌نماید که فاقد ویژگی disabled می‌باشند. در حال حاضر در اکثر مرورگرها فقط برای تگ input و textarea عمل می‌کند. 
<style>
    :enabled {
        background: yellow;
    }
</style>
<input disabled="disabled" />
<input />
<input disabled="disabled" />
<input />
در مثال فوق input دوم و چهارم به رنگ زرد نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  9.0 3.5  4.0 :enabled  3

33- :read-only
تگ‌هایی را انتخاب می‌نماید که دارای ویژگی readonly می‌باشند. در برخی مرورگرها ممکن است تگ body و سایر تگ‌هایی را که محتوای آنها غیر قابل ویرایش می‌باشد نیز انتخاب نماید.
<style>
    :read-only {
        background: yellow;
    }
</style>
<input readonly="readonly" />
<input />
<input readonly="readonly" />
<input />
در مثال فوق input اول و سوم به رنگ زرد نمایش می‌یابند. در برخی مرورگرها نیز ممکن است پس زمینه‌ی صفحه نیز به رنگ زرد نمایش یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 Yes   Yes   No -moz-  Yes :read-only  3 

34- :read-write
تگ‌هایی را انتخاب می‌نماید که فاقد ویژگی readonly می‌باشند و یا محتوای آنها قابل ویرایش است.
<style>
    :read-write {
        background: yellow;
    }
</style>
<input readonly="readonly" />
<input />
<input readonly="readonly" />
<input />
در مثال فوق input دوم و چهارم به رنگ زرد نمایش می‌یابند.  
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 Yes   Yes   No -moz-  Yes :read-write  3 

35- :valid
تگ‌های input ایی را انتخاب می‌نماید که مقدار ورودی آنها با توجه به ویژگی‌های type و pattern معتبر باشند.
مثال مربوط به این بخش در Selector بعدی، یعنی :invalid ذکر شده است.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 5.0   10.0   10.0 4.0  10.0 :valid  3 

36- :invalid
تگ‌های input ی را انتخاب می‌نماید که مقدار ورودی آنها با توجه به ویژگی‌های type و pattern نامعتبر باشند. 
<style>
    :valid {
        border: 3px solid green;
    }
    :invalid {
        border: 3px solid red;
    }
</style>
<input type="number" />
<input type="text" pattern="^[a-z]{2,10}$"/>
<input type="email" />
در مثال فوق، در ابتدا، کادر دور تگ‌های input با توجه به Selector ی با عنوان :valid، به رنگ سبز نمایش می‌یابند. با ورود اطلاعات، در صورتی که مقدار ورودی نامعتبر باشد، کادر به رنگ قرمز تغییر می‌یابد. به عنوان مثال با ورود حرف در input اول، با ورود عدد در input دوم و با ورود یک متن تا قبل از ورود @ و دامین، کادر به رنگ قرمز نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 5.0   10.0   10.0 4.0  10.0 :invalid  3 

37- :required
تگ‌هایی را انتخاب می‌نماید که دارای ویژگی required می‌باشند. 
<style>
    :required {
        background: yellow;
    }
</style>
<input required="required" />
<input />
<input required="required" />
<input />
در مثال فوق input اول و سوم به رنگ زرد نمایش می‌یابند. 
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 5.0   10.0   10.0 4.0  10.0 :required  3 

38- :optional
تگ‌هایی را انتخاب می‌نماید که فاقد ویژگی required می‌باشند.
<style>
    :optional {
        background: yellow;
    }
</style>
<input required="required" />
<input />
<input required="required" />
<input />
در مثال فوق input دوم و چهارم به رنگ زرد نمایش می‌یابند.  
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 5.0   10.0   10.0 4.0  10.0 :optional  3 

39- :in-range
برای تگ‌های input ی استفاده می‌شود که ویژگی min و max دارند. تگ هایی را انتخاب می‌نماید که مقدار ورودی آنها در بازه‌ی min و max باشند.
مثال مربوط به این بخش در Selector بعدی، یعنی :out-of-range ذکر شده است.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 5.2   11.0   No 28.0  10.0 :in-range  3

40- :out-of-range
برای تگ‌های input ی استفاده می‌شود که ویژگی min و max دارند. تگ‌هایی را انتخاب می‌نماید که مقدار ورودی آنها خارج از بازه‌ی min و max باشد.
<style>
    :in-range {
        border: 3px solid green;
    }

    :out-of-range {
        border: 3px solid red;
    }
</style>
<input type="number" min="10" max="50" />
در مثال فوق با ورود عددی خارج از محدوده‌ی 10 تا 50، کادر دور تگ به رنگ قرمز نمایش می‌یابد، در غیر اینصورت به رنگ سبز نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 5.2   11.0   No 28.0  10.0 :out-of-range  3 
اشتراک‌ها
آموزش رایگان برنامه نویسی Full-Stack به زبان فارسی

C#.NET for non-engineers.

The first course of "A Sr. Developer Course" courses. which contains:
1- C# Fundamentals for non-engineers.
2- DataBase for non-engineers.
3- Asp.NET WebForm for Non-engineers.
4- Application Architecture for no-engineers.
5- ASP.NET MVC for non-engineers.
6- Angular for non-engineers.

This is a course for who knows noting about C# and development if you know nothing about Array, variable, loop, and conditions you are in the right place.
at the end of this course, we will create one small university registration console application together.

You will learn in this course:
C#.NET
.NET Framework
Methods
Recursive methods
C# Primitive Types/Complex Types
conditions
switch case
Arrays
if statement
switch
loops
Creating a method
ref, out
enums
OOP/Object-oriented programing
Generics
Error handling
problem-solving
working with files


level: beginners to upper intermediate 

آموزش رایگان برنامه نویسی Full-Stack به زبان فارسی
مطالب
مشکل ی و ک فارسی و عربی در یک دیتابیس اس کیوال سرور

دیروز به من اطلاع دادند که در یکی از برنامه‌ها دو تا گروه "تاسیسات مکانیکی" پیدا شده!!
تاسیسات مکانیکی
تاسیسات مکانیکی

استاندارد این شرکت، استفاده از kbdfa.dll مخصوص و نسبتا قدیمی است. بنابراین استاندارد مورد استفاده همان ی و ک عربی است. (کاری ندارم خوب است یا بد، یا باید اینطور باشد یا نه، بحث این است که فعلا اینطور است و قرار نیست چیزی عوض بشود!)
در مثال فوق، ی و ک عبارت دوم فارسی است. یعنی نصب kbdfa.dll روی ویندوز تازه نصب شده، فراموش شده بوده.

راه حل‌ها:
الف) قبل از ثبت، یکسان سازی صورت گیرد. یعنی اجرای متدی شبیه به متد زیر بر روی هر ورودی متنی فارسی:

public string SafeFarsiStr(string input)
{
return input.Replace("ی", "ی").Replace("ک", "ک");
}

ب) خوب، الان که این یکسان سازی صورت نگرفته چه باید کرد؟
اسکریپتی را تهیه کرده‌ام (مخصوص SQL Server 2005 به بعد) به صورت زیر که این تبدیل را برای شما انجام می‌دهد.
به صورت خودکار تمامی فیلدهای متنی کلیه جداول دیتابیس جاری شما را یافته و ی و ک آن‌ها را یکسان می‌کند. البته همانطور که عرض شد، مطابق استاندارد این شرکت و استفاده از فایل kbdfa.dll قدیمی مورد استفاده، تمام ی و ک های فارسی به عربی تبدیل می‌شوند.

--اسکریپتی برای یک دست سازی ی و ک در تمامی رکوردهای تمامی جداول دیتابیس جاری
-- اسکریپت زیر ی و ک فارسی را به عربی تبدیل می‌کند
-- در صورت نیاز به حالت عکس ، جای مقادیر عددی یونیکد را تعویض نمائید

USE TestDb;

DECLARE @Table NVARCHAR(MAX),
@Col NVARCHAR(MAX)

DECLARE Table_Cursor CURSOR
FOR
--پیدا کردن تمام فیلدهای متنی تمام جداول دیتابیس جاری
SELECT a.name, --table
b.name --col
FROM sysobjects a,
syscolumns b
WHERE a.id = b.id
AND a.xtype = 'u' --User table
AND (
b.xtype = 99 --ntext
OR b.xtype = 35 -- text
OR b.xtype = 231 --nvarchar
OR b.xtype = 167 --varchar
OR b.xtype = 175 --char
OR b.xtype = 239 --nchar
)

OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @Table,@Col
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC (
'update [' + @Table + '] set [' + @Col +
']= REPLACE(REPLACE(CAST([' + @Col +
'] as nvarchar(max)) , NCHAR(1740), NCHAR(1610)),NCHAR(1705),NCHAR(1603)) '
)

FETCH NEXT FROM Table_Cursor INTO @Table,@Col
END CLOSE Table_Cursor DEALLOCATE Table_Cursor

توضیحات و نکاتی در مورد اسکریپت فوق:
الف) برای آشنایی با انواع XType Datatype مورد استفاده در کوئری فوق به این آدرس مراجعه نمائید.
ب) همانطور که در مطالب قبلی این وبلاگ نیز ذکر شد، امکان استفاده از تابع replace بر روی فیلدهای text و ntext وجود ندارد. هیچ اشکالی ندارد! تمام آن‌ها به nvarchar از نوع max دار cast شده و این مشکل به این صورت حل می‌شود.
ج) اس کیوال سرور اجازه تعریف یک جدول یا فیلد را به صورت متغیر بکار رفته در یک کوئری T-SQL نمی‌دهد. برای حل این موضوع باید عبارت SQL مورد نظر را به صورت یک رشته درآورد و سپس exec کرد.
د) مجبور شدم از معاد‌ل‌های عددی برای دقت بیشتر کار استفاده کنم



(در کل از تابع UNICODE اس کیوال سرور برای بدست آوردن این اعداد می‌توان استفاده کرد)

تذکر: این اسکریپت بر روی یک دیتابیس کاری تست شده و نتیجه رضایت بخش بوده؛ اما اگر شما روزی خواستید از آن استفاده کنید، حتما full backup را قبل از اجرای آن فراموش نکنید و پس از اجرا، تابع SafeFarsiStr فوق را برای ادامه کار حتما لحاظ نمائید یا از یک kbdfa.dll هماهنگ استفاده کنید.


مطالب
Blazor 5x - قسمت نهم - مبانی Blazor - بخش 6 - ساده سازی تعاریف ویژگی‌های المان‌ها و انتقال پارامترها به چندین زیر سطح
بررسی ویژگی Attribute Splatting

برای تعریف المان‌های فرم‌ها نیاز است ویژگی‌های قابل توجهی را مانند placeholder ،required ،maxlength و غیره، تعریف کرد که در صورت زیاد بودن تعداد المان‌های یک فرم، مدیریت تعریف این ویژگی‌ها مشکل می‌شود. به همین جهت قابلیت ویژه‌ای مخصوص اینکار به نام Attribute Splatting در Blazor درنظر گرفته شده‌است. برای توضیح آن، ابتدا کامپوننت والد Pages\LearnBlazor\AttributeSplatting.razor و کامپوننت فرزند Pages\LearnBlazor\LearnBlazor‍Components\AttributeSplattingChild.razor را ایجاد می‌کنیم.
در کامپوننت فرزند یا همان AttributeSplattingChild، یک المان را به همراه تعدادی ویژگی تعریف شده مشاهده می‌کنید:
<div>
    <h4 class="text-primary pt-3">Attribute Splatting Child Component</h4>

    <input id="roomName"
        placeholder="@Placeholder"
        required="@Required"
        maxlength="@MaxLength"
        class="form-control" />
</div>

@code {
    [Parameter]
    public string Placeholder { get; set; } = "Initial Text";

    [Parameter]
    public string Required { get; set; } = "required";

    [Parameter]
    public string MaxLength { get; set; } = "10";
}
و کامپوننت والد و یا همان AttributeSplatting.razor، از آن به صورت زیر استفاده می‌کند:
@page "/AttributeSplatting"

<h1>Attribute Splatting</h1>

<AttributeSplattingChild
    Placeholder="Enter the Room Name From Parent"
    MaxLength="5">
</AttributeSplattingChild>
روش ارسال پارامترها را به کامپوننت‌های فرزند، در قسمت پنجم این سری بررسی کردیم. تنها نکته‌ی جدید آن، تعریف مقادیر پیش‌فرض پارامترها در کامپوننت فرزند است. برای مثال در حین تعریف المان AttributeSplattingChild در کامپوننت والد، پارامتر Required مقدار دهی نشده‌است. در این حالت، مقدار پیش‌فرض درج شده‌ی در کامپوننت فرزند، مورد استفاده قرار می‌گیرد؛ وگرنه مقادیر تنظیم شده‌ی توسط کامپوننت والد، حق تقدم بالاتری نسبت به مقادیر پیش‌فرض خواهند داشت.

مشکل! کامپوننت AttributeSplattingChild که فقط به همراه یک المان است، تا این لحظه نیاز به تعریف سه پارامتر جدید را جهت تامین ویژگی‌های آن المان داشته‌است. اگر تعداد این المان‌ها افزایش پیدا کرد، آیا راه بهتری برای مدیریت تعداد بالای ویژگی‌های مورد نیاز وجود دارد؟
پاسخ: در یک چنین حالتی می‌توان ویژگی‌های هر المان را توسط پارامتری از نوع Dictionary مدیریت کرد؛ بجای تعریف تک تک آن‌ها به صورت خواصی مجزا. به این قابلیت، Attribute Splatting می‌گویند.
در این حالت تمام کدهای AttributeSplattingChild.razor به صورت زیر خلاصه می‌شوند:
<div>
    <h4 class="text-primary pt-3">Attribute Splatting Child Component</h4>

    <input id="roomName" @attributes="InputAttributes" class="form-control" />
</div>

@code {
    [Parameter]
    public Dictionary<string, object> InputAttributes { get; set; } = new Dictionary<string, object>
    {
        { "required" , "required"},
        { "placeholder", "Initial Text"},
        { "maxlength", 10}
    };
}
در اینجا با استفاده از دایرکتیو جدید attributes@ می‌توان لیستی از key/value‌های ویژگی‌های یک المان را به صورت یک دیکشنری دریافت کرد و دیگر نیازی نیست تا تک تک آن‌ها را تبدیل به یک پارامتر و خاصیت عمومی مجزا کرد. در این حالت مقادیری که در سمت کامپوننت فرزند تعریف می‌شوند، به عنوان مقادیر اولیه‌ی قابل بازنویسی توسط کامپوننت والد، درنظر گرفته خواهند شد (مانند مثال پارامتر Required که عنوان شد).
و همچنین در ادامه کامپوننت والد یا AttributeSplatting.razor نیز به صورت زیر تغییر می‌کند:
@page "/AttributeSplatting"

<h1>Attribute Splatting</h1>

<AttributeSplattingChild InputAttributes="InputAttributesFromParent"></AttributeSplattingChild>

@code{
    Dictionary<string, object> InputAttributesFromParent = new Dictionary<string, object>
    {
        { "required" , "required"},
        { "placeholder", "Enter the Room Name From Parent"},
        { "maxlength", 5}
    };
}
با توجه به اینکه پارامتر InputAttributes، یک شیء دیکشنری را دریافت می‌کند، فیلد آن‌را در قسمت کدهای کامپوننت جاری تعریف کرده و مورد استفاده قرار می‌دهیم. در این حالت هر مقداری که در سمت والد تنظیم شود، حق تقدم بیشتری نسبت به مقدار پیش‌فرض ویژگی‌های تنظیم شده‌ی در کامپوننت فرزند خواهد داشت.



ساده سازی روش تعریف key/value‌های شیء دیکشنری Attribute Splatting

تا اینجا موفق شدیم تعداد قابل ملاحظه‌ای از پارامترهای عمومی یک کامپوننت را تنها توسط یک شیء Dictionary مدیریت کنیم. همچنین همانطور که ملاحظه می‌کنید، هم Dictionary سمت کامپوننت فرزند و هم سمت کامپوننت والد، نیاز به مقدار دهی اولیه‌ای را دارند. این مقدار دهی اولیه را می‌توان به نحو دیگری نیز در حین استفاده‌ی از قابلیت Attribute Splatting، انجام داد:
<div>
    <h4 class="text-primary pt-3">Attribute Splatting Child Component</h4>

    <input id="roomName" @attributes="InputAttributes" placeholder="Initial Text" class="form-control" />
</div>

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; } = new Dictionary<string, object>();
}
در اینجا مقادیر اولیه‌ی دیکشنری تعریف شده را حذف کرده‌ایم و بجای آن‌ها، این مقادیر اولیه را به صورت ویژگی‌های متداول یک المان HTML ای تعریف کرده‌ایم؛ مانند placeholder تعریف شده. برای اینکه یک چنین ویژگی‌هایی به عنوان key/valueهای دیکشنری تعریف شده قابل استفاده باشند، تنها کافی است خاصیت CaptureUnmatchedValues ویژگی پارامتر را به true تنظیم کرد. در اینجا Unmatched Values، همان ویژگی‌هایی هستند که در حین تعریف یک المان اضافه شده‌اند (مانند placeholder در مثال فوق) اما در حین مقدار دهی اولیه‌ی دیکشنری، تعریف نشده‌اند و یا تمام پارامترهای عمومی دیگری که در اینجا ذکر و تعریف نشده‌اند. بنابراین تنها یک CaptureUnmatchedValues = true را در سطح یک کامپوننت می‌توان تعریف کرد.

پس از این تغییر، کامپوننت والد هم به صورت زیر خلاصه می‌شود و دیگر نیازی به تعریف و مقدار دهی InputAttributes و یا تعریف مجزای یک دیکشنری را ندارد. در اینجا هر ویژگی که به المان نسبت داده شود، به عنوان Unmatched Values تفسیر شده و مورد استفاده قرار می‌گیرد.
@page "/AttributeSplatting"

<h1>Attribute Splatting</h1>

<AttributeSplattingChild placeholder="Placeholder default"></AttributeSplattingChild>


اگر به تصویر فوق دقت کنید، هرچند در کامپوننت والد مقدار placeholder، به متن دیگری تنظیم شده، اما متن تنظیم شده‌ی در کامپوننت فرزند، تقدم بیشتری پیدا کرده و نمایش داده شده‌است. علت اینجا است که محل قرارگیری آن در مثال فوق، در سمت راست دایرکتیو attributes@ است. اگر آن‌را در سمت چپ attributes@ قرار دهیم، حق تقدم attributes@ بیشتر شده و مقدار تنظیم شده‌ی در سمت کامپوننت والد، بجای placeholder اولیه‌ی تعریف شده‌ی در اینجا مورد استفاده قرار می‌گیرد:
<input id="roomName" placeholder="Initial Text" @attributes="InputAttributes" class="form-control" />


روش انتقال پارامترها به چندین زیر سطح

در قسمت قبل، ParentComponent.razor و ChildComponent.razor را تعریف و تکمیل کردیم. هدف از آ‌ن‌ها، بررسی ویژگی Render Fragment‌ها بود. در ادامه‌ی آن، یک زیر کامپوننت دیگر را نیز به نام Pages\LearnBlazor\LearnBlazor‍Components\GrandChildComponent.razor اضافه می‌کنیم. هدف این است که کامپوننت Parent، کامپوننت Child را فراخوانی کند و کامپوننت Child، کامپوننت GrandChild را تا یک سلسله مراتب از کامپوننت‌ها را تشکیل دهیم.
محتوای GrandChildComponent را هم بسیار ساده نگه می‌داریم، تا پارامتری رشته‌ای را دریافت کرده و نمایش دهد:
<div class="row">
    <h4 class="text-primary pl-4 pt-2 col-12">Grand Child Component</h4>
    <br />
    <p> There is a message - @MessageForGrandChild </p>
</div>

@code {
    [Parameter]
    public string MessageForGrandChild { get; set; }
}
در ChildComponent، کامپوننت GrandChild را به نحو زیر فراخوانی کرده و پارامتری را به آن ارسال می‌کنیم:
<div class="mt-2">
    <GrandChildComponent MessageForGrandChild="@MessageForGrandChild"></GrandChildComponent>
</div>


@code {
    [Parameter]
    public string MessageForGrandChild { get; set; }

   // ...
}
و اکنون در بالاترین سطح این سلسه مراتبی که مشاهده می‌کنید یعنی کامپوننت Parent، این پیام MessageForGrandChild را مقدار دهی خواهیم کرد تا توسط GrandChildComponent نمایش داده شود:
<ChildComponent
    MessageForGrandChild="This is a message from Grand Parent"
    Title="This is the second child component">
    <p><b>@MessageText</b></p>
</ChildComponent>
همانطور که مشاهده می‌کنید، انتقال متداول یک پارامتر، از بالاترین سطح سلسه مراتب کامپوننت‌ها به پایین‌ترین سطح موجود، نیاز به مقدار قابل ملاحظه‌ای کد تکراری را دارد. همچنین برای نمونه پارامتر انتقالی تعریف شده‌ی در کامپوننت Child، اصلا در آن کامپوننت استفاده نمی‌شود و هدف از آن، متصل کردن یک سطح بالاتر، به یک سطح پایین‌تر است.
بنابراین اکنون این سؤال مطرح می‌شود که آیا می‌توان پارامتری را در همان کامپوننت Parent تعریف کرد که توسط کامپوننت GrandChild قابل شناسایی و استفاده باشد، بدون اینکه کامپوننت Child را در این بین تغییر دهیم؟
پاسخ: بله. برای اینکار ویژگی‌های CascadingValue و CascadingParameter در Blazor پیش بینی شده‌اند.
در ابتدا، پارامتر MessageForGrandChild کامپوننت Child حذف کرده و سپس آن‌را توسط کامپوننت توکار CascadingValue محصور می‌کنیم. در اینجا نیاز است مقدار انتقالی را نیز مشخص کنیم:
<CascadingValue Value="@MessageForGrandChild">
    <ChildComponent        
        Title="This is the second child component">
        <p><b>@MessageText</b></p>
    </ChildComponent>
</CascadingValue>

@code {
    string MessageForGrandChild = "This is a message from Grand Parent";
پس از این تعریف، به کامپوننت Child مراجعه کرده و پارامتر MessageForGrandChild آن‌را حذف می‌کنیم؛ چون دیگر نیازی به آن نیست. همچنین در این کامپوننت، فراخوانی GrandChildComponent نیز به صورت زیر خلاصه شده و دیگر نیازی به ذکر پارامتر انتقالی MessageForGrandChild حذف شده را ندارد:
<GrandChildComponent></GrandChildComponent>
در آخر به کامپوننت GrandChild مراجعه کرده و اینبار پارامتر مورد استفاده‌ی در آن‌را با ویژگی جدید CascadingParameter مزین می‌کنیم:
[CascadingParameter]
public string MessageForGrandChild { get; set; }


چند نکته:
- در اینجا نوع CascadingParameter تعریف شده، باید با نوع Value کامپوننت CascadingValue، در بالاترین سطح سلسله مراتب کامپوننت‌ها، یکی باشد.
- نام CascadingParameter تعریف شده مهم نیست. فقط نوع آن مهم است.
- تمام کامپوننت‌های موجود و پوشش داده شده‌ی در سلسله مراتب جاری، قابلیت تعریف CascadingParameter ای مانند مثال فوق را دارند و این تعریف، محدود به پایین‌ترین سطح موجود نیست. برای مثال در اینجا در کامپوننت Child هم در صورت نیاز می‌توان همین CascadingParameter را تعریف و استفاده کرد.


روش تعریف پارامترهای آبشاری نام‌دار

تا اینجا روش انتقال یک پارامتر را از بالاترین سطح، به پایین‌ترین سطح سلسله مراتب کامپوننت‌های تعریف شده، بررسی کردیم. اکنون شاید این سؤال مطرح شود که اگر خواستیم بیش از یک پارامتر را بین اجزای این سلسله، به اشتراک بگذاریم چه باید کرد؟
در این حالت می‌توان پارامتر جدید را توسط یک کامپوننت CascadingValue تو در تو، به صورت زیر معرفی کرد؛ که اینبار نامدار نیز هست:
<CascadingValue Value="@MessageForGrandChild" Name="MessageFromGrandParent">
    <CascadingValue Value="@Number" Name="GrandParentsNumber">
        <ChildComponent
            Title="This is the second child component">
            <p><b>@MessageText</b></p>
        </ChildComponent>
    </CascadingValue>
</CascadingValue>

@code {
    string MessageForGrandChild = "This is a message from Grand Parent";
    int Number = 7;
برای نمونه در این مثال، عدد 7 نیز قرار است در اختیار سلسله مراتب شروع شده‌ی از کامپوننت جاری، قرار گیرد. به همین جهت یک CascadingValue تو در توی مختص آن نیز تعریف شده‌است که اینبار نامش GrandParentsNumber است.

پس از این تغییر، GrandChildComponent، این پارامترهای نامدار را از طریق ذکر صریح خاصیت Name ویژگی CascadingParameter، دریافت می‌کند:
<div class="row">
    <h4 class="text-primary pl-4 pt-2 col-12">Grand Child Component</h4>
    <br />
    There is a message: @Message
    <br />
    GrandParentsNumber: @Number
</div>

@code {
    [CascadingParameter(Name = "MessageFromGrandParent")]
    public string Message { get; set; }

    [CascadingParameter(Name = "GrandParentsNumber")]
    public int Number { get; set; }
}


یک نکته: چون نوع پارامترهای ارسالی یکی نیست، الزامی به ذکر نام آن‌ها نبود. در این حالت بر اساس نوع پارامترهای آبشاری، عملیات اتصال مقادیر صورت می‌گیرد. اما اگر نوع هر دو را برای مثال رشته‌ای تعریف می‌کردیم، مقدار Number، بر روی مقدار MessageForGrandChild بازنویسی می‌شد. یعنی در UI، هر دو پارامتر هم نوع، یک مقدار را نمایش می‌دادند که در حقیقت مقدار پایین‌ترین CascadingValue تعریف شده‌است. بنابراین ذکر نام پارامترهای آبشاری، روشی‌است جهت تمایز قائل شدن بین پارامترهای هم نوع.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-09.zip
مطالب دوره‌ها
ارتباطات بلادرنگ و SignalR
زمانیکه صحبت از برنامه‌های بلادرنگ می‌شود با کاربرانی سر و کار داریم که نیاز دارند تا اطلاعات مورد نیاز خود را همواره و بلافاصله در آخرین وضعیت به روز آن مشاهده کنند. در این بین، کلاینت می‌خواهد یک برنامه وب باشد یا سیلورلایت و یا یک برنامه نوشته شده با WPF. حتی برنامه‌های موبایل را نیز باید به این لیست اضافه کرد.
در اینجا کلمه بلادرنگ به معنای ارسال اطلاعات از طرف سرور به کلاینت‌ها با فاصله زمانی بسیار کوتاهی از به روز رسانی اطلاعات صورت گرفته در سمت سرور است.
نمونه‌ای از این برنامه‌ها شامل موارد ذیل هستند:
- اطلاع رسانی همزمان به گروهی از کاربران
- جستجوهای زنده و به روز رسانی‌هایی از این دست
- نمایش بلادرنگ قیمت‌ها و وضعیت تجاری محصولات و سهام‌ها
- بازی‌های تعاملی
- برنامه‌های گروهی و تعاملی (مانند برنامه‌های Chat)
- برنامه‌های شبکه‌های اجتماعی (برای مثال پیام جدیدی دارید؛ شخص خاصی آنلاین یا آفلاین شد و امثال آن)

بنابراین به صورت خلاصه قصد داریم به ارائه بازخوردها و اطلاع رسانی‌های بلادرنگ یا نسبتا سریع و به روز از سمت سرور به کلاینت‌ها برسیم.
برای مثال یک دیتاگرید را درنظر بگیرید. دو کاربر در شبکه صفحه یکسانی را گشوده‌اند و یکی از آن‌ها مشغول به ویرایش و یا حذف اطلاعات است. در ارتباطات بلادرنگ کاربر یا کاربران دیگر نیز باید (یا بهتر است) در زمانیکه گرید یکسانی را گشوده‌اند، بلافاصله آخرین تغییرات را ملاحظه کنند. یا حتی حالتی را درنظر بگیرید که شخصی SQL Server management studio را گشوده و در آنجا مشغول به تغییر اطلاعات گردیده است. در این حالت نیز بهتر است آخرین تغییرات بلافاصله به اطلاع کاربران رسانده شوند.

معرفی الگوی Push service

البته باید دقت داشت که الگوی push service یک الگوی رسمی ذکر شده در گروه‌های مرسوم الگوهای طراحی نیست، اما مفهوم آن سرویسی است که چندین کار ذیل را انجام می‌دهد:
الف) پذیرش اتصالات از چندین مصرف کننده. مصرف کننده‌ها در اینجا الزاما محدود به کلاینت‌های وب یا دسکتاپ نیستند؛ می‌توانند حتی یک سرور یا سرویس دیگر نیز باشند.
ب) قادر است اطلاعات را به مصرف کننده‌های خود ارسال کند. این سرویس می‌تواند یک برنامه ASP.NET باشد یا حتی یک سرویس متداول ویندوز.
ج) در اینجا چندین منبع خارجی مانند یک بانک اطلاعاتی یا تغییرات رخ داده توسط یک سخت افزار که می‌توانند سبب بروز رخدادهایی در push service گردند نیز می‌تواند وجود داشته باشند. هر زمان که تغییری در این منابع خارجی رخ دهد، مایل هستیم تا مصرف کننده‌ها را مطلع سازیم.


پروتکل HTTP و ارتباطات بلادرنگ

پروتکلی که در ارتباطات بلادرنگ مبتنی بر SignalR مورد استفاده قرار می‌گیرد، HTTP است و از قابلیت‌های Request و Response آن در اینجا بیشترین بهره برده می‌شود. پیاده سازی Push عموما بر مبنای یکی از روش‌های متداول زیر است:
1) Periodic polling
به این معنا که مثلا هر 10 ثانیه یکبار، کاری را انجام بده؛ مانند ارسال متناوب: آیا تغییری رخ داده؟ آیا تغییری رخ داده؟ و .... به همین ترتیب. این روش اصلا بهینه نبوده و منابع زیادی را خصوصا در سمت سرور مصرف خواهد کرد. برای مثال:
function getInfo() {
         $.ajax("url", function ( newInfo){
                  if ( newInfo != null) {
                      // do something with the data
                  }
         });
    // poll again after 20 seconds
    setTimeout(getInfo,20000);
}
// start the polling loop
getInfo();

2) Long polling
به آن HTTP Streaming یا Comet هم گفته می‌شود. این روش نسبتا هوشمند بوده و کلاینت اتصالی را به سرور برقرار خواهد کرد. سرور در این حالت تا زمانیکه اطلاعاتی را در دسترس نداشته باشد، پاسخی نخواهد داد. برای نمونه:
function getNewInfo(){
  $.ajax("url", function (newinfo) {
      // do something with the data
  // start the new request
      getNewINfo();
  });
}
// start the polling loop
getNewInfo();

این روش نسبت به حالت Periodic polling بهینه‌تر است اما نیاز به اتصالات زیادی داشته و همچنین تردهای بسیاری را در سمت سرور به خود مشغول خواهد کرد.

3) Forever frame
فقط در IE پشتیبانی می‌شود. در این روش یک Iframe مخفی توسط مرورگر تشکیل شده و از طریق آن درخواستی به سرور ارسال می‌شود. سپس سرور متناوبا با تزریق اسکریپت‌هایی به این Iframe سبب فراخوانی مجدد وضعیت خود می‌گردد. در این روش نیز به ازای هر درخواست و پاسخ، ارتباطات گشوده و بسته خواهند شد.

4) Server Sent Events یا SSE
این مورد جزو استاندارد HTML5 است. در اینجا اتصالی برقرار شده و داده‌ها از طریق اتصالات HTTP منتقل می‌شوند.
var eventSrc = new EventSource("url");
    // register event handler for the message
    eventSrc.addEventListener( "message",function (evt) {
    //process the data
});
این روش نیز بسیار شبیه به حالت long polling است. سرور تا زمانیکه اطلاعاتی را برای پاسخ دهی فراهم نداشته باشد، اتصال را باز نگه می‌دارد. به این ترتیب از لحاظ مقیاس پذیری گزینه بهتری است (نسبت به حالتیکه مدام اتصال برقرار و قطع می‌شود). اکثر مرورگرها منهای نگارش‌های قدیمی IE از این روش پشتیبانی می‌کنند.
تنها تفاوت آن با حالت long polling در این است که پس از ارائه پاسخ به کلاینت، اتصال را قطع نمی‌کند. Long polling نیز اتصال را باز نگه می‌دارد، اما این اتصال را بلافاصله پس از ارائه پاسخ، می‌بندد.

5) Web sockets
Web sockets در سکوی کاری ویندوز، تنها در ویندوز‌های 8، ویندوز سرور 2012 و دات نت 4 و نیم پشتیبانی می‌شود. هرچند این روش در حال حاضر به عنوان بهترین روش Push مطرح است اما به دلیل محدودیتی که یاد شد، مدتی طول خواهد کشید تا استفاده گسترده‌ای پیدا کند.
var socket = new WebSocket("url");
socket.onmessage = function (msg) {
var newInfo = msg.data;
// do something with the data
}
// client can also send request to server
socket.send(.... )
با این اوصاف آیا راه حل بهتر و میانه‌تری وجود دارد؟
بلی. اگر به وضعیت فعلی سکوی کاری ASP.NET نگاه کنیم:

SignalR را می‌توان مشاهده کرد که در گروه ساخت سرویس‌های آن قرار گرفته است. همانطور که ملاحظه می‌کنید، این سرویس جدید آنچنان وابستگی به سایر اجزای آن نداشته و می‌تواند خارج از ASP.NET نیز مورد استفاده قرار گیرد.

SignalR چیست؟

SignalR راه حلی است سمت سرور برای نوشتن push services. همچنین به همراه کتابخانه‌های سمت کاربری است که ارتباطات push services را در انواع و اقسام سکوهای کاری میسر می‌سازد. SignalR سورس باز بوده و برای اعمال غیرهمزمان (asynchronous) بهینه سازی شده است.
SignalR بر اساس مدل ذهنی اتصالات ماندگار (persistent connections) طراحی شده است. اتصالات ماندگار را باید به عنوان اتصالاتی سریع و غیرطولانی درنظر گرفت. در اینجا Signal یک اتصال است که اطلاعاتی به آن ارسال می‌گردد و هدف، انتقال قطعات کوچکی از اطلاعات است و هدف، ارسال حجم عظیمی از اطلاعات نیست. برای مثال اطلاع رسانی سریعی صورت گیرد که تغییراتی رخ داده است و سپس ادامه کار و دریافت اطلاعات واقعی توسط فرآیندهای متداول مثلا HTTP GET انجام شود. البته باید دقت داشت SignalR نیز نهایتا از یکی از 5 روش push بررسی شده در این قسمت استفاده می‌کند. اما بر اساس توانایی‌های کلاینت و سرور، به صورت هوشمند بهترین و بهینه‌ترین انتخاب را به کاربر ارائه می‌دهد.
اتصالات ماندگار قسمت سطح پایین SignalR را تشکیل می‌دهند. سطح بالاتر آن که این مفاهیم را به شکلی کپسوله شده ارائه می‌دهد، Hubs نام دارد که پایه اصلی دوره جاری را تشکیل خواهد داد.



همانطور که عنوان شد، SignalR سورس باز بوده و دارای مخزن کدی عمومی در GitHub است. همچنین بسته‌های تشکیل دهنده‌ی آن از طریق NuGet نیز قابل دریافت هستند. این بسته‌ها شامل هسته SignalR و کلاینت‌های آن مانند کلاینت‌های WinRT، سیلورلایت، jQuery، ویندوز فون8 و امثال آن هستند.

شروع کار با SignalR

تیم SignalR مثالی مقدماتی از نحوه کار با SignalR را به صورت یک بسته NuGet ارائه داده‌اند که از طریق آدرس و فرمان ذیل قابل دریافت است:
 PM> Install-Package Microsoft.AspNet.SignalR.Sample
قبل از اینکه این مثال را دریافت کنید نیاز است ابتدا یک برنامه ASP.NET جدید را آغاز نمائید (تفاوتی نمی‌کند که MVC باشد یا Web forms). سپس دستور فوق را فراخوانی کنید.

پس از دریافت مثال، یکبار پروژه را کامپایل کرده و سپس بر روی فایل StockTicker.html آن کلیک راست نموده و گزینه مشاهده در مرورگر را انتخاب کنید. همچنین برای اینکه این مثال را بهتر مشاهده کنید، بهتر است دو وهله از مرورگر را باز کرده و آدرس باز شده را در آن بررسی کنید تا اعمال تغییرات همزمان به کلاینت‌های متفاوت را بهتر بتوان بررسی و مشاهده کرد.

مطالب
استفاده از LINQ جهت جستجوی فایل‌ها

یکی دیگر از کاربردهای anonymous types ، امکان استفاده از قابلیت‌های LINQ برای جستجوی فایل‌ها و پوشه‌ها است.
مثال:

using System;
using System.Linq;
using System.IO;

namespace LINQtoDir
{
class Program
{
static void Main(string[] args)
{
var query = from f in new DirectoryInfo(@"C:\Documents and Settings\vahid\My Documents\My Pictures")
.GetFiles("*.*", SearchOption.AllDirectories)
where f.Extension.ToLower() == ".png" || f.Extension.ToLower() == ".jpg"
orderby f.LastAccessTime
select new
{
DateLastModified = f.LastWriteTime,
Extension = f.Extension,
Size = f.Length,
FileName = f.Name
};

foreach (var file in query)
Console.WriteLine(file.FileName);

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}

در این مثال توسط کوئری نوشته شده، تمامی تصاویر jpg و یا png موجود در پوشه my pictures یافت شده و سپس بر اساس LastAccessTime مرتب می‌شوند. در آخر با استفاده از anonymous types ، یک شیء IEnumerable از خواص مورد نظر فایل‌های یافت شده، بازگشت داده می‌شود. اکنون هر استفاده‌ی دلخواهی را می‌توان از این شیء انجام داد.

اشتراک‌ها
درک Coupling و Cohesion

I invited some of my friends to discuss the nebulous concepts of coupling and cohesion in software design. How do we think about these topics? How do we understand the terms? How do we use that in our work as programmers? How do we teach it to others? How much does any of it even matter?


Our invited guests: Corey Haines, Curtis Cooley, Dale Emery, J. B. Rainsberger, Jim Weirich, Kent Beck, Nat Pryce, Ron Jeffries. 

درک Coupling و Cohesion
مطالب
StringBuilder

بهترین روش برای تولید و دستکاری یک رشته (string) طولانی و یا دستکاری متناوب و تکراری یک رشته استفاده از کلاس StringBuilder است. این کلاس در فضای نام System.Text قرار داره. شی String در دات‌نت‌فریمورک تغییرناپذیره (immutable)، بدین معنی که پس از ایجاد نمی‌توان محتوای اونو تغییر داد. برای مثال اگر شما بخواین محتوای یک رشته رو با اتصال به رشته‌ای دیگه تغییر بدین، اجازه اینکار را به شما داده نمی‌شه. درعوض به‌صورت خودکار رشته‌ای جدید در حافظه ایجاد میشه و محتوای دو رشته موجود پس از اتصال به هم درون اون قرار می‌گیره. این کار درصورتی‌که تعداد عملیات مشابه زیاد باشه می‌تونه تاثیر منفی بر کارایی و حافظه خالی در دسترس برنامه بگذاره.

کلاس StringBuilder با استفاده از آرایه‌ای از کاراکترها، راه‌حل مناسب و بهینه‌ای رو برای این مشکل فراهم کرده. این کلاس در زمان اجرا به شما اجازه می‌ده تا بدون ایجاد نمونه‌های جدید از کلاس String، محتوای یک رشته رو تغییر بدین. شما می‌تونید نمونه‌ای از این کلاس رو به‌صورت خالی و یا با یک رشته اولیه ایجاد کنید، سپس با استفاده از متدهای متنوع موجود، محتوای رشته رو با استفاده از انواع داده مختلف و به‌صورت دلخواه دستکاری کنید. هم‌چنین با استفاده از متد معروف  ()ToString این کلاس می‌تونید در هر لحظه دلخواه رشته تولیدی رو بدست بیارین. دو پراپرتی مهم کلاس StringBuilder رفتارش رو درهنگام افزودن داده‌های جدید کنترل می‌کنن:

Capacity , Length

پراپرتی Capacity اندازه بافر کلاس StringBuilder را تعیین می‌کنه و Length طول رشته جاری موجود در این بافر رو نمایش می‌ده. اگر پس از افزودن داده جدید، طول رشته از اندازه بافر موجود بیشتر بشه، StringBuilder باید یه بافر جدید با اندازه‌ای مناسب ایجاد کنه تا رشته جدید رو بتونه تو خودش نگه داره. اندازه این بافر جدید به‌صورت پیش‌فرض دو برابر اندازه بافر قبلی درنظر گرفته می‌شه. بعد تمام رشته قبلی رو تو این بافر جدید کپی میکنه.

از برنامه ساده زیر میتونین برای بررسی این مسئله استفاده کنین:

using System.IO;
using System.Text;

class Program
{
  static void Main()
  {
    using (var writer = new StreamWriter("data.txt"))
    {
      var builder = new StringBuilder();
      for (var i = 0; i <= 256; i++)
      {
        writer.Write(builder.Capacity);
        writer.Write(",");
        writer.Write(builder.Length);
        writer.WriteLine();
        builder.Append('1'); // <-- Add one character
      }
    }
  }
}

دقت کنین که برای افزودن یک کاراکتر استفاده از دستور Append با نوع داده char (همونطور که در بالا استفاده شده) بازدهی بهتری نسبت به استفاده از نوع داده string (با یک کاراکتر) داره. خروجی کد فوق به صورت زیره:

16, 0
16, 1
16, 2
...
16,14
16,15
16,16
32,17
...

استفاده نامناسب و بی‌دقت از این کلاس می‌تونه منجر به بازسازی‌های متناوب این بافر شده که درنهایت فواید کلاس StringBuilder رو تحت تاثیر قرار میده. درهنگام کار با کلاس StringBuilder اگر از طول رشته موردنظر و یا حد بالایی برای Capacity آن آگاهی حتی نسبی دارین، می‌تونید با مقداردهی مناسب این پراپرتی از این مشکل پرهیز کنید.

نکته: مقدار پیش‌فرض پراپرتی Capacity برابر 16 است.

هنگام مقداردهی پراپرتی‌های Capacity یا Length به موارد زیر توجه داشته باشید:

- مقداردهی Capacity به میزانی کمتر از طول رشته جاری (پراپرتی Length)، منجر به خطای زیر می‌شه:

System.ArgumentOutOfRangeException

خطای مشابهی هنگام مقداردهی پراپرتی Capacityبه بیش از مقدار پراپرتی MaxCapacity رخ می‌دهه.البته این مورد تنها درصورتی‌که بخواین اونو به بیش از حدود 2 گیگابایت (Int32.MaxValue) مقداردهی کنید پیش میاد!

- اگر پراپرتی Length را به مقداری کمتر از طول رشته جاری تنظیم کنید، رشته به اندازه طول تنظیمی کوتاه (truncate) میشه.

- اگر مقدار پراپرتی Length را به میزانی بیشتر از طول رشته جاری تنظیم کنید، فضای خالی موجود در بافر با space پر میشه.

- تنظیم مقدار Length بیشتر از Capacity، منجر به مقداردهی خودکار پراپرتی Capacity به مقدار جدید تنظیم شده برای Length میشه.

در ادامه به یک مثال برای مقایسه کارایی تولید یک رشته طولانی با استفاده از این کلاس میپردازیم. تو این مثال از دو روش برای تولید رشته‌های طولانی استفاده میشه. روش اول که همون روش اتصال رشته‌ها (Concat) به هم هستش و روش دوم هم که استفاده از کلاس StringBuilder است. در قطعه کد زیر کلاس مربوط به عملیات تست رو مشاهده میکنین:

namespace StringBuilderTest
{
  internal class SbTest1
  {
    internal Action<string> WriteLog;
    internal int Iterations { get; set; }
    internal string TestString { get; set; }

    internal SbTest1(int iterations, string testString, Action<string> writeLog)
    {
      Iterations = iterations;
      TestString = testString;
      WriteLog = writeLog;
    }

    internal void StartTest()
    {
      var watch = new Stopwatch();

      //StringBuilder
      watch.Start();
      var sbTestResult = SbTest();
      watch.Stop();
      WriteLog(string.Format("StringBuilder time: {0}", watch.ElapsedMilliseconds));

      //Concat
      watch.Start();
      var concatTestResult = ConcatTest();
      watch.Stop();
      WriteLog(string.Format("ConcatTest time: {0}", watch.ElapsedMilliseconds));

      WriteLog(string.Format("Results are{0} the same", sbTestResult == concatTestResult ? string.Empty : " NOT"));
    }

    private string SbTest()
    {
      var sb = new StringBuilder(TestString);
      for (var x = 0; x < Iterations; x++)
      {
        sb.Append(TestString);
      }
      return sb.ToString();
    }

    private string ConcatTest()
    {
      string concat = TestString;
      for (var x = 0; x < Iterations; x++)
      {
        concat += TestString;
      }
      return concat;
    }
  }
}

دو روش بحث‌شده در کلاس مورد استفاده قرار گرفته و مدت زمان اجرای هر کدوم از عملیات‌ها به خروجی فرستاده میشه. برای استفاده از این کلاس هم میشه از کد زیر در یک برنامه کنسول استفاده کرد:

do
{
  Console.Write("Iteration: ");
  var iterations = Convert.ToInt32(Console.ReadLine());
  Console.Write("Test String: ");
  var testString = Console.ReadLine();
  var test1 = new SbTest1(iterations, testString, Console.WriteLine);
  test1.StartTest();
  Console.WriteLine("----------------------------------------------------------------");
} while (Console.ReadKey(true).Key == ConsoleKey.C); // C = continue 

برای نمونه خروجی زیر در لپ‌تاپ من (Corei7 2630QM) بدست اومد:

تنظیم خاصیت Capacity به یک مقدار مناسب میتونه تو کارایی تاثیرات زیادی بگذاره. مثلا در مورد مثال فوق میشه یه متد دیگه برای آزمایش تاثیر این مقداردهی به صورت زیر به کلاس برناممون اضافه کنیم:

private string SbCapacityTest()
{
  var sb = new StringBuilder(TestString) { Capacity = TestString.Length * Iterations };
  for (var x = 0; x < Iterations; x++)
  {
    sb.Append(TestString);
  }
  return sb.ToString();
}

تو این متد قبل از ورود به حلقه مقدار خاصیت Capacity به میزان موردنظر تنظیم شده و نتیجه بدست اومده:

مشاهده میشه که روش concat خیلی کنده (دقت کنین که طول رشته اولیه هم بیشتر شده) و برای ادامه کار مقایسه اون رو کامنت کردم و نتایج زیر بدست اومد:

می‌بینین که استفاده مناسب از مقداردهی به خاصیت Capacity میتونه تا حدود 300 درصد سرعت برنامه ما رو افزایش بده. البته همیشه اینطوری نخواهد بود. ما در این مثال مقدار دقیق طول رشته نهایی رو میدونستیم که باعث میشه عملیات افزایش بافر کلاس StringBuilder هیچوقت اتفاق نیفته. این امر در واقعیت کمتر پیش میاد.

مقاله موجود در سایت dotnetperls شکل زیر رو به عنوان نتیجه تست بازدهی ارائه میده:

- در مواقعی که عملیاتی همچون مثال بالا طولانی و حجیم ندارین بهتره که از این کلاس استفاده نکنین چون عملیات‌های داخلی این کلاس در عملیات کوچک و سبک (مثل ابتدای نمودار فوق) موجب کندی عملیات میشه. همچنین استفاده از اون نیاز به کدنویسی بیشتری داره.

- این کلاس فشار کمتری به حافظه سیستم وارد میکنه. درمقابل استفاده از روش concat موجب اشغال بیش از حد حافظه میشه که خودش باعث اجرای بیشتر و متناوب‌تر GC میشه که در نهایت کارایی سیستم رو کاهش میده.

- استفاده از این کلاس برای عملیات Replace (و یا عملیات مشابه) در حلقه‌ها جهت کار با رشته‌های طولانی و یا تعداد زیادی رشته میتونه بسیار سریعتر و بهتر عمل کنه چون این کلاس برخلاف کلاس string اشیای جدید تولید نمیکنه.

- یه اشتباه بزرگ در استفاده از این کلاس استفاده از "+" برای اتصال رشته‌های درون StringBuilder هست. هرگز از این کارها نکنین. (فکر کنم واضحه که چرا)