نظرات مطالب
استفاده از افزونه‌ی jsTree در ASP.NET MVC
سلام من این تایپیک‌ها رو بررسی کردم ولی نتونستم این فرایندها رو ادغام کنم ببینید من داخل همین پروژه شما دو تا مدل با نام‌های Category.cs وDataBaseContext.cs تعریف کردم و کد هاشم بصورت زیر است حالا چه جوری با تابع بازگشتی داخل HomeController بجای populatetree اظلاعات رو دریافت کنم در ضمن فیلدها هم طبق گفته خودتون باید مطابق jstree باشه که در مل‌ها تعریف کردم؟
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;

namespace MvcJSTree.Models.Entities
{
    public class Category
    {
        public Category()
        {

        }
        public Category(string id,string parentid,string orginialid,string text,string position,string href)
        {
            this.Id = id;
            this.ParentId = parentid;
            this.OriginalId = orginialid;
            this.Text = text;
            this.Position = position;
            this.Href = href;
        }
               
        public string Id { set; get; }
        public string ParentId { set; get; }
        public string OriginalId { set; get; }
        public string Text { set; get; }
        public string Position { set; get; }
        public string Href { set; get; }

        public override string ToString()
        {
            return string.Format("{0},{1},{2},{3},{4},{5}",this.Id,this.ParentId,this.OriginalId,this.Text,this.Position,this.Href);
        }      
    }
    
}

کد مربوط به DataBaseContext
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Data.Entity;

namespace MvcJSTree.Models
{
    public class DataBaseContext:System.Data.Entity.DbContext
    {
        public DataBaseContext()
        {

        }
         static DataBaseContext ()
        {

            System.Data.Entity.Database.SetInitializer(
                new System.Data.Entity.DropCreateDatabaseIfModelChanges<DataBaseContext>());

        }
        
         public System.Data.Entity.DbSet<DataBaseContext> Category { get; set; }
        
    }
}
مطالب
سرعت واکشی اطلاعات در List و Dictionary
دسترسی به داده‌ها پیش شرط انجام همه‌ی منطق‌های اکثر نرم افزار‌های تجاری می‌باشد. داده‌های ممکن در حافظه ، پایگاه داده ، فایل‌های فیزیکی و هر منبع دیگری قرار گرفته باشند.
هنگامی که حجم داده‌ها کم باشد شاید روش دسترسی و الگوریتم مورد استفاده اهمیتی نداشته باشد اما با افزایش حجم داده‌ها روش‌های بهینه‌تر تاثیر مستقیم در کارایی برنامه دارند.
در این مثال سعی بر این است که در یک سناریوی خاص تفاوت بین Dictionary و List را بررسی کنیم :
فرض کنید 2 کلاس Student  و Grade موجود است که وظیفه‌ی نگهداری اطلاعات دانش آموز و نمره را بر عهده دارند.
    public class Grade
    {
        public Guid StudentId { get; set; }
        public string Value { get; set; }

        public static IEnumerable<Grade> GetData()
        {
            for (int i = 0; i < 10000; i++)
            {
                yield return new Grade
                                 {
                                     StudentId = GuidHelper.ListOfIds[i], Value = "Value " + i
                                 };
            }
        }
    }

    public class Student
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Grade { get; set; }

        public static IEnumerable<Student> GetStudents()
        {
            for (int i = 0; i < 10000; i++)
            {
                yield return new Student
                                 {
                                     Id = GuidHelper.ListOfIds[i],
                                     Name = "Name " + i
                                 };
            }
        }
    }
از کلاس GuidHelper برای تولید و نگهداری شناسه‌های یکتا برای دانش آموز کمک گرفته شده است :
    public class GuidHelper
    {
        public static List<Guid> ListOfIds=new List<Guid>();

        static GuidHelper()
        {
            for (int i = 0; i < 10000; i++)
            {
                ListOfIds.Add(Guid.NewGuid());
            }
        }
    }
سپس لیستی از دانش آموزان و نمرات را درون حافظه ایجاد کرده و با یک حلقه  نمره‌ی هر دانش آموز به Property مورد نظر مقدار داده می‌شود.

ابتدا از LINQ روی لیست برای پیدا کردن نمره‌ی مورد نظر استفاده کرده و در روش دوم برای پیدا کردن نمره‌ی هر دانش آموز از Dictionary  استفاده شده :
    internal class Program
    {
        private static void Main(string[] args)
        {
            var stopwatch = new Stopwatch();
            List<Grade> grades = Grade.GetData().ToList();
            List<Student> students = Student.GetStudents().ToList();

            stopwatch.Start();
            foreach (Student student in students)
            {
                student.Grade = grades.Single(x => x.StudentId == student.Id).Value;
            }
            stopwatch.Stop();
            Console.WriteLine("Using list {0}", stopwatch.Elapsed);
            stopwatch.Reset();
            students = Student.GetStudents().ToList();
            stopwatch.Start();
            Dictionary<Guid, string> dictionary = Grade.GetData().ToDictionary(x => x.StudentId, x => x.Value);

            foreach (Student student in students)
            {
                student.Grade = dictionary[student.Id];
            }
            stopwatch.Stop();
            Console.WriteLine("Using dictionary {0}", stopwatch.Elapsed);
            Console.ReadKey();
        }
    }
نتیجه‌ی مقایسه در سیستم من اینگونه می‌باشد :



همانگونه که مشاهده می‌شود در این سناریو خواندن نمره از روی Dictionary بر اساس 'کلید' بسیار سریع‌تر از انجام یک پرس و جوی LINQ روی لیست است.

زمانی که از LINQ on list
   student.Grade = grades.Single(x => x.StudentId == student.Id).Value;
برای پیدا کردن مقدار مورد نظر یک به یک روی اعضا لیست حرکت می‌کند تا به مقدار مورد نظر برسد در نتیجه پیچیدگی زمانی آن O n هست. پس هر چه میزان داده‌ها بیشتر باشد این روش کند‌تر می‌شود.

زمانی که از Dictonary
         student.Grade = dictionary[student.Id];
برای پیدا کردن مقدار استفاده می‌شود با اولین تلاش مقدار مورد نظر یافت می‌شود پس پیچیدگی زمانی آن O 1 می‌باشد.

در نتیجه اگر نیاز به پیدا کردن اطلاعات بر اساس یک مقدار یکتا یا کلید باشد تبدیل اطلاعات به Dictionary و خواندن از آن بسیار به صرفه‌تر است.

تفاوت این 2 روش وقتی مشخص می‌شود که میزان داده‌ها زیاد باشد.

در همین رابطه (1 ، 2

DictionaryVsList.zip
مطالب
کلاس‌ها در ES 6
رسمی‌ترین زبان‌های شیء گرا از کلاس‌ها و وراثت مربوط به آنها پشتیبانی می‌کنند؛ ولی از زمانی که JavaScript ساخته شد، به دلیل نداشتن کلاس‌ها باعث سردرگمی بیشتر توسعه دهنده‌ها شد. برای آشنایی با مباحث شیء گرایی در جاوااسکریپت  ^ و را مطالعه کنید.
در واقع کلاس‌ها در ES 6 هم واقعا مانند کلاس‌ها در سایر زبان‌ها نبوده و صرفا یک syntax آسان بر فراز روش‌های پیاده سازی انواع داده‌های شخصی در ورژن‌ها قبلی می‌باشند. این syntax به معنای تولید مدل جدید شیء گرایی در JavaScript نمی‌باشد و در ادامه خواهیم دید که این کلاس‌ها چیزی بجز یک function نیستند. در ورژن‌های قبل ES، تعریف نوع داده جدید به عنوان مثال به شکل زیر بود:
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
‫در کد بالا که مربوط است به ورژن 5 اکما اسکریپت، PersonType یک تابع سازنده است که دارای یک پراپرتی به نام name و یک متد در سطح آبجکت به نام sayName میباشد.
Class declarations یکی از روش‌های تعریف کلاس در ES 6 میباشد. به عنوان مثال در ورژن جدید، تعریف کلاس مثال فوق به شکل زیر خواهد بود:
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"
در کد بالا این بار به جای تعریف یک  تابع (function) به عنوان سازنده، برای ساخت نوع داده‌ی شخصی، خواهید توانست به صورت مستقیم این سازنده را درون کلاس خود با نام constructor که مشخصا برای این منظور در نظر گرفته شده است، تعریف کنید. همانطور که در خطوط آخر کد بالا مشخص است، کلاس PersonClass چیزی بجز یک function نیست و همین مورد گفته‌های ابتدایی مطلب را تأیید می‌کند.  باید توجه داشت که در تعریف هر کلاسی فقط یک تابع سازنده با نام constructor می‌تواند وجود داشته باشد؛ در غیر این صورت خطای syntax error را دریافت خواهیم کرد.
شباهت‌هایی و معادل‌هایی که در پیاده سازی مثال بالا در دو ورژن مختلف وجود دارد باعث خواهد شد که بدون نگرانی از اینکه با کدام ورژن کار می‌کنید، به صورت ترکیبی از آنها استفاده کنید.
Class Expressions روش دوم پیاده سازی کلاس‌ها در ES 6 می‌باشد؛ به دو صورت named و unnamed که به صورتیکه در زیر مشاهده می‌کنید، قابل تعریف خواهد بود:
//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
همانطور که متوجه شدید، class‌ها به همانند function‌ها به دو شکل declarations و expressions قابل تعریف هستند (یکی دیگر از شباهت ها). یک نکته در حالت تعریف به صورت named این است که میتوان PerssonClass2 و PerssonClass را به دلیل اینکه هر دوی آنها اشاره‌گر به یک کلاس هستند، به جای هم استفاده کنید.
نکته جالب این که class expressions‌ها را می‌توان به عنوان آرگومان توابع دیگر هم ارسال کرد؛ برای مثال :
function createObject(classDef) {
    return new classDef();
}

let obj = createObject(class {
    sayHi() {
        console.log("Hi!");
    }
});

obj.sayHi();        // "Hi!"
در کد بالا createObject، متدی است که class expression ما به عنوان آرگومان آن پاس داده شده است و در نهایت توانسته‌ایم از این کلاس پاس داده شده در داخل متد نمونه سازی کرده و آن را به عنوان نتیجه‌ی برگشتی return کنیم. 
نکته جالب دیگر این که با استفاده از class expressions‌ها خواهیم توانست singleton‌ها را با فراخوانی بلافاصله‌ی سازنده کلاس، پیاده سازی کنیم. برای این منظور باید کلمه‌ی کلیدی new را قبل از کلمه‌ی کلیدی class نوشته و در پایان هم از دو پرانتز باز و بسته استفاده کنید که معادل فراخوانی سازنده‌ی کلاس خواهد بود.
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 معرفی کنیم.

مطالب
نکاتی توصیه ای برای برنامه نویسی اندروید : قسمت دوم
در ادامه‌ی قسمت اول، ده مورد دیگر از نکات کاربردی را بیان می‌کنیم.

  یازده. در جاوا رویدادها با استفاده از اینترفیس‌ها پیاده سازی می‌شوند. برای نامگذاری یک رویداد، قاعده آن در جاوا بدین شکل است که نام‌ها به صورت (+ ) Camel نوشته شده و آخرین عبارت هم Listener باشد و نیازی هم به حرف I در نامگذاری اینترفیس نیست؛ چون همه می‌دانند که این Listener آخری یعنی رویدادی که با اینترفیس پیاده سازی شده است و استفاده از I بی معنی است. هر چند بر خلاف دات نت، در اینجا استفاده از قاعده I چندان متداول نیست.
 public interface CopyFileListener
    {
        void PublishProgress(long fileSize,long copiedSize);
    }

دوازده. گوگل اینترفیس‌هایی را که برای رویدادها میسازد، داخل کلاس اصلی تعریف میکند. پس بهتر هست که شما هم همین روند را ادامه بدید و از این قاعده خارج نشوید. اگر خوب دقت کرده باشید، در برنامه نویسی اندروید تمام اینترفیس‌ها داخل کلاس اصلی هستند:
 textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
            }
        });
در کد بالا رویداد OnclickListener در خود کلاس View تعریف شده است. پس ما هم بهتر هست همین کار را بکنیم:
public class MemoryWare {

    public interface CopyFileListener
    {
        void PublishProgress(long fileSize,long copiedSize);
    }
....
}
یک نکته دیگر اینکه موقعی که یک رویداد را به یک پراپرتی set انتساب می‌دهیم، رسم این است که نام آن پراپرتی با عبارت SetOn آغاز شود و با نام اینترفیس پایان یابد:
SetOnClickListener
البته اگر کلاس شما لیستی از رویدادها را درست میکند بهتر است از عبارت Add به جای SetOn استفاده کنید و برای آن یک Remove هم قرار دهید. نمونه آن را می‌توانید در کد زیر ببینید:
 editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

سیزده
. آداپتورها و آداپتور ویوها (چون لیست) قسمت مهمی از برنامه‌های اندرویدی به شمار می‌آیند؛ تا حدی که در بیشتر برنامه‌های ساده هم حضور پررنگی دارند. ولی برای استفاده از این آداپتورها باید بدانید که نحوه کار آن‌ها چگونه است. بسیاری از کاربران در این قسمت اشتباهات زیادی می‌کنند. اگر در stackOverflow هم در اینباره نگاه کنید، با حجم انبوهی از سوالات روبرو می‌شوید و فقط به خاطر اینکه نحوه کارکرد آن را نمی‌دانند، به مشکل برخورده‌اند.
کلاس BaseAdapter اصلی‌ترین کلاس آداپتور هاست که بقیه از آن مشتق شده‌اند و معروفترین مشتقات آن، کلاس‌های CursorAdapter و ArrayAdapter هستند که امکانات بیس آداپتور را افزایش داده‌اند.به عنوان مثال در کد پایین از ArrayAdapter استفاده شده است.


نحوه کار یک آداپتور بدین صورت است که متدی را به نام GetView با قابلیت override دارد که با هر تعداد آیتم موجود صدا زده می‌شود. ولی اگر تصور کنیم فقط چند صدهزار آیتم هم داشته باشیم، آیا واقعا اجرا می‌شود؟ جواب این سوال این است که با هر بار اسکرولی که شما میکنید آیتم‌های بعدی ایجاد می‌شوند ولی باز این سوال پیش می‌آید که هر آیتم برای خود جداگانه تشکیل می‌شود؟ مطمئنا جواب خیر است. آداپتورها از سیستمی به نام ViewRecycler برای کش کردن آیتم‌های ایجاد شده استفاده می‌کنند و با هر اسکرولی که انجام می‌شود آیتم‌های بعدی از روی آیتم‌های قبلی که قبلا از صفحه خارج شده‌اند، ساخته می‌شوند و آیتم‌های کش شده قبلی را با پارامتری با نام convertView به دست شما می‌رساند.
کد زیر را ببینید:
  @Override
    public View getView(int position, View rowView, ViewGroup parent) {

        ViewHolder viewHolder=null;
        if(rowView==null)
        {
            // 1. Create inflater
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            // 2. Get rowView from inflater
            rowView = inflater.inflate(R.layout.row_bank_group_list, parent, false);
            viewHolder=new ViewHolder();
            viewHolder.txtGroupName=(TextView) rowView.findViewById(R.id.text_groupName);
            rowView.setTag(viewHolder);
        }
        else
        {
            viewHolder=(ViewHolder)rowView.getTag();
        }
        viewHolder.txtGroupName.setText(getItem(position).getName());
        viewHolder.txtGroupName.setTypeface(new FontSystem().get_General_Font(context));
        viewHolder.txtGroupName.setTextColor(context.getResources().getColor(R.color.black));

        return rowView;
    }
کد بالا ابتدا بررسی میکند که آیا convertView نال است یا خیر. اگر نال بود به این معنا است که کش برای شما چیزی نداشته است و باید آیتم جدیدی را بسازید. پس اشیاء موجود در آن را در حافظه می‌آورید و مقداردهی می‌کنید. ولی اگر برابر نال نباشد، یعنی کش حاوی یک سری آیتم است که قبلا ایجاد شده‌اند. پس نیاز به inflate کردن مجدد ندارد و میتوانید  همان را مستقیما مورد استفاده قرار دهید و با مقادیر جدید آن را ست کنید.
کلاس داخلی ViewHolder هم یک الگو برای عدم بررسی Viewهای داخل آن است که نیازی به یافتن و تبدیل مجدد آن‌ها نداشته باشید. در این روش شیء، داخل خصوصیت tag آیتم قرار گرفته است و وقتی از کش برداشته شود، خاصیت تگ آن را می‌خوانیم و مستقیما مورد استفاده قرار می‌دهیم. در این حالت شما بهترین استفاده را از پردازش‌ها و حافظه، می‌کنید.

چهارده. یکی از کارهایی را که قبل از کار کردن در یک مسیر فیزیکی باید انجام دهید این است که مطمئن باشید اجازه نوشتن در آن ناحیه را دارید یا خیر. در غیر اینصورت برنامه شما با خطای FC روبرو می‌شود و اجرای آن خاتمه می‌یابد. به همین دلیل اکثر برنامه نویسان از متد CanWrite در کلاس File استفاده می‌کنند. ولی در هنگام استفاده از این متد باید دقت داشته باشید که کلاس File فقط باید حاوی مسیر باشد و اسمی از فایل مربوطه در آن نباشد. دلیل هم آن است که این احتمال می‌رود اگر فایلی هم وجود نداشته باشد، مقدار false را به شما برگرداند. مثال زیر قرار است فایلی را در کارت حافظه بنویسید، ولی بررسی اجازه نوشتن در مسیر، اشتباه است:
File file=new File(sdcardPath,fileName);

if(file.CanWrite())
{
  .....
}
کد بالا را به طور صحیح بازنویسی می‌کنیم:
File file=new File(sdcardPath);

if(file.CanWrite())
{
file=new File(sdcardPath,filePath);
  .....
}
اگر هنگام تست این کد مشکلی نداشتید، دلیل بر صحت کد نیست. بلکه بنابر تجربه شخصی من، زمانی این مشکل پیش آمده بود که روی چند گوشی تست شده و بعدها مشکل در گوشی پیش آمده بود که هم مدل و دقیقا مشابه یکی از گوشی‌های تستی بود.

پانزده. کارت حافظه خارجی: همه برنامه نویسان اندروید حداقل یکبار از کد زیر استفاده کرده اند:
Environment.getExternalStorageDirectory()
بررسی‌ها در استک اورفلو نشان می‌دهد که برنامه نویسان، گزارش عملکرد اشتباهی را از این متد دارند. ولی حقیقت این است که این متد به هیچ عنوان مقدار اشتباهی را بر نمی‌گرداند. بلکه منطق آن متفاوت از چیزی است که شما فکر می‌کنید. وقتی ما صحبت از حافظه خارجی برای یک گوشی میکنیم، منظور همان کارت حافظه‌ای است که به طور جداگانه داخل گوشی قرار داده‌ایم و انتظار داریم متد بالا آدرس و مدخل همین کارت را برای ما فراهم کند. ولی در کمال تعجب می‌بینیم که آدرس حافظه داخلی برگردانده می‌شود. پس باید ببینیم اندروید از  آن چه انتظاری دارد؟
هر برنامه‌ای که در اندروید نصب می‌شود در مسیر
/Data/Data
یک دایرکتوری با نام خود دارد مثل:
/Data/Data/Info.Dotnettips.MyApp
این دایرکتوری تنها متعلق به این برنامه بوده و کسی جز Root به آن دسترسی ندارد. اندروید این دایرکتوری را به عنوان حافظه داخلی در نظر میگیرد که برای کار با آن نیاز به هیچ آدرس دهی نیست. ولی در کنار این دایرکتوری حافظه داخلی خود گوشی که در آن انبوه فایل‌های خود را ذخیره می‌کنید هم هست که اندروید آن را حافظه خارجی می‌پندارد. حال آن حافظه‌ای را که شما جداگانه به صورت یک کارت یا USB OTG متصل میکنید، به عنوان حافظه خارجی در نظر نمیگیرد. در صورتی که شما چنین انتظاری را دارید، برای حل این مشکل بهتر است از کدهای موجود مثل کد زیر استفاده کنید:
    /**
     * it will returns sd path for you
     *  <p>
     *  <b>Required Permission: </b>android.permission.READ_EXTERNAL_STORAGE<br/>
     * </p>
     * @return
     */
    public  List<String> GetExternalMounts() {
        final List<String> out = new ArrayList<>();
        String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
        String s = "";
        try {
            final Process process = new ProcessBuilder().command("mount")
                    .redirectErrorStream(true).start();
            process.waitFor();
            final InputStream is = process.getInputStream();
            final byte[] buffer = new byte[1024];
            while (is.read(buffer) != -1) {
                s = s + new String(buffer);
            }
            is.close();
        } catch (final Exception e) {
            e.printStackTrace();
        }

        // parse output
        final String[] lines = s.split("\n");
        for (String line : lines) {
            if (!line.toLowerCase(Locale.US).contains("asec")) {
                if (line.matches(reg)) {
                    String[] parts = line.split(" ");
                    for (String part : parts) {
                        if (part.startsWith("/"))
                            if (!part.toLowerCase(Locale.US).contains("vold"))
                                if(new File(part).canWrite())
                                    out.add(part);
                    }
                }
            }
        }
        return out;
    }

شانزده.
یکی از روش‌های انتقال اطلاعات بین اکتیویتی‌ها مختلف استفاده از Extras هاست که شما با تعیین یک نام یا کلید، اطلاعات مربوطه را ارسال و توسط همان کلید؛ اطلاعات را در اکتیویتی مقصد دریافت میکنید:
notesIntent.putExtra("PartyId", PartyId);
startActivity(notesIntent);
و در مقصد:
PartyId=getIntent().getLongExtra("PartyId",0);
در این حالت بهتر است با این رشته‌ها نیز همانند مورد شماره دو در قسمت اول رفتار شود تا نیازی به نوشتن و تکرار این نام‌ها نباشد. ولی با یک نگاه به استانداردهای نوشته شده در خود اندروید و بسیاری از کتابخانه‌های ثالث در می‌یابیم که بهترین روش این است که این کلید‌ها را به صورت متغیرهای ایستا در خود اکتیویتی ذخیره کنیم؛ در این حالت هر کلید در جایگاه واقعی خودش قرار گرفته است. نمونه‌ای از این استفاده را می‌توانید در کتابخانه FilePicker مشاهده کنید:
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false);
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_FILE);

هفده.
قواعد نامگذاری: برای نامگذاری متغیرها از قانون CamelCase استفاده میکنیم. ولی برای حالات زیر از روش‌های دیگر استفاده می‌شود:
  • برای ثابت‌ها از حروف بزرگ و _ استفاده کنید.
  • برای متغیرهای خصوصی از حرف m در ابتدای نام متغیر استفاده کنید.
  • برای متغیرهای استاتیک از حرف s در ابتدای نام متغیر استفاده کنید.
نمونه ای از این نامگذاری که توسط مستندات گوگل سفارش شده است:
public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

هجده:
قاعده نظم و ترتیب در import‌ها توسط مستندات گوگل بدین شکل تعریف شده است:
  1. نام پکیج‌های ارائه شده توسط گوگل
  2. نام پکیج‌های ثالث
  3. نام پکیج‌های موجود در java و javax
  4. پکیج‌های موجود در پکیج اصلی
البته رعایت این قاعده کمی سخت و عموما بدون اجراست ولی نگران نباشید. از آنجایی که ادیتور از طرف jet brains ارائه شده‌است و عمل import به طور خودکار صورت میگیرد و با ابزارهای دیگری که در دل این ادیتور قرار گرفته است، این عمل به طور خودکار انجام می‌گیرد.

نوزدهم. مرتب سازی متدهای دسترسی یک کلاس: بسیار خوب است که همیشه کدهای ما نظم خاصی را داشته باشد تا پیگیری‌های شخصی و تیمی در آن راحت‌تر صورت بگیرد. برای مثال در یک کلاس ابتدا متدهای public و سپس private قرار گیرند و الی آخر.
الگوی عمومی که برای کار با جاوا صورت گرفته است به شکل زیر می‌باشد:
public, protected, private,abstract, static, transient, volatile, synchronized, final, native.
البته این متدهای دسترسی بسته به فیلد بودن و متد بودن نیز تغییر میکند. به عنوان مثال ابتدا فیلدها در نظر گرفته می‌شوند و سپس متدها و ...
ادیتور intelij شامل تنظمیاتی برای مرتب سازی کدهاست که در این مورد بسیار سودمند است. با طی کردن مسیر زیر می‌توانید برای آن ترتیب اینگونه موارد را مشخص کنید.
Settings>Editor>Code Style>Arrangement
در اینجا شما امکان تعاریف این ترتیب‌ها را دارید. البته بهتر هست در حالت پیش فرض باشد تا حالتی عمومی بین افراد مختلف برقرار باشد.

در تصویر بالا متدها به ترتیب متدهای دستری بین بلوک‌های کامنت method start و method end قرار گرفته اند.

 همچنین شامل گزینه‌های دیگری نیز میباشد که به نظرم فعال کردنشان بسیار خوب است. گزینه keep overridden methods together به شما کمک می‌کند تا متدهایی را که رونویسی می‌شوند، در کنار یکدیگر قرار بگیرند که برای کلاس‌های اندرویدی مثل اکتیویتی‌ها و فرگمنت‌ها و ... بسیار خوب است. گزینه مفید دیگر Keep dependent methods together است که در دو حالت عمقی یا خطی متدهای وابسته (متدهایی که متدهای دیگر را در آن کلاس صدا میزنند) در کنار یکدیگر قرار میدهد و مابقی گزینه‌ها، که بسیار سودمند هست. به هر حال هر قاعده‌ای را که برای خود انتخاب میکنید اگر در حالت پیش فرض نیست بهتر است در مستندات پروژه ذکر شود تا افراد دیگر سریعتر به موضوع پی ببرند.

قسمت بیستم. این مورد برای افراد تازه کار می‌باشد که تازه اندروید استادیو را باز کرده‌اند و مشغول کدنویسی می‌باشند. یکی از مواردی که در همان مرحله اول به آن برمیخورید این است که intellisense  ادیتور به بزرگی و کوچکی حروف حساس است و تنها با حرف اول سازگاری دارد. برای تغییر این مسئله باید مسیر زیر را طی کنید:
Settings>Editor>Completion>Case-sensitive Completion>None
حالا با تایپ هر کلمه، دستورات و آبجکت‌هایی را که شامل آن کلمات باشند، به شما نمایش داده خواهند شد.

مطالب
روش استفاده‌ی صحیح از HttpClient در برنامه‌های دات نت
اگر در کدهای خود قطعه کد ذیل را دارید:
using(var client = new HttpClient())
{
   // do something with http client
}
استفاده‌ی از using در اینجا، نه‌تنها غیرضروری و اشتباه است، بلکه سبب از کار افتادن زود هنگام برنامه‌ی شما با صدور استثنای ذیل خواهد شد:
 Unable to connect to the remote server
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.


HttpClient خود را Dispose نکنید

کلاس HttpClient اینترفیس IDisposable را پیاده سازی می‌کند. بنابراین روش استفاده‌ی اصولی آن باید به صورت ذیل و با پیاده سازی خودکار رهاسازی منابع مرتبط با آن باشد:
using (var client = new HttpClient())
{
       var result = await client.GetAsync("http://example.com/");
}
اما در این حال فرض کنید به همین روش تعدادی درخواست را ارسال کرده‌اید:
for (int i = 0; i < 10; i++)
{
      using (var client = new HttpClient())
      {
            var result = await client.GetAsync("http://example.com/");
            Console.WriteLine(result.StatusCode);
      }
}
مشکل این روش، در ایجاد سوکت‌های متعددی است که حتی پس از بسته شدن برنامه نیز باز، باقی خواهند ماند:
  TCP    192.168.1.6:13996      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:13997      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:13998      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:13999      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:14000      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:14001      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:14002      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:14003      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:14004      93.184.216.34:http     TIME_WAIT
  TCP    192.168.1.6:14005      93.184.216.34:http     TIME_WAIT
این یک نمونه‌ی خروجی برنامه‌ی فوق، توسط دستور netstat «پس از بسته شدن کامل برنامه» است.

بنابراین اگر برنامه‌ی شما تعداد زیادی کاربر دارد و یا تعداد زیادی درخواست را به روش فوق ارسال می‌کند، سیستم عامل به حد اشباع ایجاد سوکت‌های جدید خواهد رسید.
این مشکل نیز ارتباطی به طراحی این کلاس و یا زبان #C و حتی استفاده‌ی از using نیز ندارد. این رفتار، رفتار معمول سیستم عامل، با سوکت‌های ایجاد شده‌است. TIME_WAIT ایی را که در اینجا ملاحظه می‌کنید، به معنای بسته شدن اتصال از طرف برنامه‌ی ما است؛ اما سیستم عامل هنوز منتظر نتیجه‌ی نهایی، از طرف دیگر اتصال است که آیا قرار است بسته‌ی TCP ایی را دریافت کند یا خیر و یا شاید در بین راه تاخیری وجود داشته‌است. برای نمونه ویندوز به مدت 240 ثانیه یک اتصال را در این حالت حفظ خواهد کرد، که مقدار آن نیز در اینجا تنظیم می‌شود:
 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]

بنابراین روش توصیه شده‌ی کار با HttpClient، داشتن یک وهله‌ی سراسری از آن در برنامه و عدم Dispose آن است. HttpClient نیز thread-safe طراحی شده‌است و دسترسی به یک شیء سراسری آن در برنامه‌های چند ریسمانی مشکلی را ایجاد نمی‌کند. همچنین Dispose آن نیز غیرضروری است و پس از پایان برنامه به صورت خودکار توسط سیستم عامل انجام خواهد شد.


تمام اجزای HttpClient به صورت Thread-safe طراحی نشده‌اند

تا اینجا به این نتیجه رسیدیم که روش صحیح کار کردن با HttpClient، نیاز به داشتن یک وهله‌ی Singleton از آن‌را در سراسر برنامه دارد و Dispose صریح آن، بجز اشباع سوکت‌های سیستم عامل و ناپایدار کردن تمام برنامه‌هایی که از آن سرویس می‌گیرند، حاصلی را به همراه نخواهد داشت. در این بین مطابق مستندات HttpClient، استفاده‌ی از متدهای ذیل این کلاس thread-safe هستند:
CancelPendingRequests
DeleteAsync
GetAsync
GetByteArrayAsync
GetStreamAsync
GetStringAsync
PostAsync
PutAsync
SendAsync
اما تغییر این خواص در کلاس HttpClient به هیچ عنوان thread-safe نبوده و در برنامه‌های چند ریسمانی و چند کاربری، مشکل ساز می‌شوند:
BaseAddress
DefaultRequestHeaders
MaxResponseContentBufferSize
Timeout
بنابراین در طراحی کلاس مدیریت کننده‌ی HttpClient برنامه‌ی خود نیاز است به ازای هر BaseAddress‌، یک HttpClient خاص آن‌را ایجاد کرد و HttpClientهای سراسری نمی‌توانند BaseAddress‌های خود را نیز به اشتراک گذاشته و تغییری را در آن ایجاد کنند.


استفاده‌ی سراسری و مجدد از HttpClient، تغییرات DNS را متوجه نمی‌شود

با طراحی یک کلاس مدیریت کننده‌ی سراسری HttpClient با طول عمر Singelton، به یک مشکل دیگر نیز برخواهیم خورد: چون در اینجا از اتصالات، استفاده‌ی مجدد می‌شوند، دیگر تغییرات DNS را لحاظ نخواهند کرد.
برای حل این مشکل، در زمان ایجاد یک HttpClient سراسری، به ازای یک BaseAddress مشخص، باید از ServicePointManager کوئری گرفته و زمان اجاره‌ی اتصال آن‌را دقیقا مشخص کنیم:
var sp = ServicePointManager.FindServicePoint(new Uri("http://thisisasample.com"));
sp.ConnectionLeaseTimeout = 60*1000; //In milliseconds
با این‌کار هرچند هنوز هم از اتصالات استفاده‌ی مجدد می‌شود، اما این استفاده‌ی مجدد، نامحدود نبوده و مدت معینی را پیدا می‌کند.


طراحی یک کلاس، برای مدیریت سراسری وهله‌های HttpClient‌

تا اینجا به صورت خلاصه به نکات ذیل رسیدیم:
- HttpClient باید به صورت یک وهله‌ی سراسری Singleton مورد استفاده قرار گیرد. هر وهله سازی مجدد آن 35ms زمان می‌برد.
- Dispose یک HttpClient غیرضروری است.
- HttpClient تقریبا thread safe طراحی شده‌است؛ اما تعدادی از خواص آن مانند BaseAddress‌  اینگونه نیستند.
- برای رفع مشکل اتصالات چسبنده (اتصالاتی که هیچگاه پایان نمی‌یابند)، نیاز است timeout آن‌را تنظیم کرد.

بنابراین بهتر است این نکات را در یک کلاس به صورت ذیل کپسوله کنیم:
using System;
using System.Collections.Generic;
using System.Net.Http;

namespace HttpClientTips
{
    public interface IHttpClientFactory : IDisposable
    {
        HttpClient GetOrCreate(
            Uri baseAddress,
            IDictionary<string, string> defaultRequestHeaders = null,
            TimeSpan? timeout = null,
            long? maxResponseContentBufferSize = null,
            HttpMessageHandler handler = null);
    }
}

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;

namespace HttpClientTips
{
    /// <summary>
    /// Lifetime of this class should be set to `Singleton`.
    /// </summary>
    public class HttpClientFactory : IHttpClientFactory
    {
        // 'GetOrAdd' call on the dictionary is not thread safe and we might end up creating the HttpClient more than
        // once. To prevent this Lazy<> is used. In the worst case multiple Lazy<> objects are created for multiple
        // threads but only one of the objects succeeds in creating the HttpClient.
        private readonly ConcurrentDictionary<Uri, Lazy<HttpClient>> _httpClients =
                         new ConcurrentDictionary<Uri, Lazy<HttpClient>>();
        private const int ConnectionLeaseTimeout = 60 * 1000; // 1 minute

        public HttpClientFactory()
        {
            // Default is 2 minutes: https://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.dnsrefreshtimeout(v=vs.110).aspx
            ServicePointManager.DnsRefreshTimeout = (int)TimeSpan.FromMinutes(1).TotalMilliseconds;
            // Increases the concurrent outbound connections
            ServicePointManager.DefaultConnectionLimit = 1024;
        }

        public HttpClient GetOrCreate(
           Uri baseAddress,
           IDictionary<string, string> defaultRequestHeaders = null,
           TimeSpan? timeout = null,
           long? maxResponseContentBufferSize = null,
           HttpMessageHandler handler = null)
        {
            return _httpClients.GetOrAdd(baseAddress,
                             uri => new Lazy<HttpClient>(() =>
                             {
                                 // Reusing a single HttpClient instance across a multi-threaded application means
                                 // you can't change the values of the stateful properties (which are not thread safe),
                                 // like BaseAddress, DefaultRequestHeaders, MaxResponseContentBufferSize and Timeout.
                                 // So you can only use them if they are constant across your application and need their own instance if being varied.
                                 var client = handler == null ? new HttpClient { BaseAddress = baseAddress } :
                                               new HttpClient(handler, disposeHandler: false) { BaseAddress = baseAddress };
                                 setRequestTimeout(timeout, client);
                                 setMaxResponseBufferSize(maxResponseContentBufferSize, client);
                                 setDefaultHeaders(defaultRequestHeaders, client);
                                 setConnectionLeaseTimeout(baseAddress, client);
                                 return client;
                             },
                             LazyThreadSafetyMode.ExecutionAndPublication)).Value;
        }

        public void Dispose()
        {
            foreach (var httpClient in _httpClients.Values)
            {
                httpClient.Value.Dispose();
            }
        }

        private static void setConnectionLeaseTimeout(Uri baseAddress, HttpClient client)
        {
            // This ensures connections are used efficiently but not indefinitely.
            client.DefaultRequestHeaders.ConnectionClose = false; // keeps the connection open -> more efficient use of the client
            ServicePointManager.FindServicePoint(baseAddress).ConnectionLeaseTimeout = ConnectionLeaseTimeout; // ensures connections are not used indefinitely.
        }

        private static void setDefaultHeaders(IDictionary<string, string> defaultRequestHeaders, HttpClient client)
        {
            if (defaultRequestHeaders == null)
            {
                return;
            }
            foreach (var item in defaultRequestHeaders)
            {
                client.DefaultRequestHeaders.Add(item.Key, item.Value);
            }
        }

        private static void setMaxResponseBufferSize(long? maxResponseContentBufferSize, HttpClient client)
        {
            if (maxResponseContentBufferSize.HasValue)
            {
                client.MaxResponseContentBufferSize = maxResponseContentBufferSize.Value;
            }
        }

        private static void setRequestTimeout(TimeSpan? timeout, HttpClient client)
        {
            if (timeout.HasValue)
            {
                client.Timeout = timeout.Value;
            }
        }
    }
}
در اینجا به ازای هر baseAddress جدید، یک HttpClient خاص آن ایجاد می‌شود تا در کل برنامه مورد استفاده‌ی مجدد قرار گیرد. برای مدیریت thread-safe ایجاد HttpClientها نیز از نکته‌ی مطلب «الگویی برای مدیریت دسترسی همزمان به ConcurrentDictionary» استفاده شده‌است. همچنین نکات تنظیم ConnectionLeaseTimeout و سایر خواص غیر thread-safe کلاس HttpClient نیز در اینجا لحاظ شده‌اند.

پس از تدارک این کلاس، نحوه‌ی معرفی آن به سیستم باید به صورت Singleton باشد. برای مثال اگر از ASP.NET Core استفاده می‌کنید، آن‌را به صورت ذیل ثبت کنید:
namespace HttpClientTips.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IHttpClientFactory, HttpClientFactory>();
            services.AddMvc();
        }

اکنون، یک نمونه، نحوه‌ی استفاده‌ی از اینترفیس IHttpClientFactory تزریقی به صورت ذیل می‌باشد:
namespace HttpClientTips.Web.Controllers
{
    public class HomeController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;
        public HomeController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task<IActionResult> Index()
        {
            var host = new Uri("http://localhost:5000");
            var httpClient = _httpClientFactory.GetOrCreate(host);
            var responseMessage = await httpClient.GetAsync("home/about").ConfigureAwait(false);
            var responseContent = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
            return Content(responseContent);
        }
سرویس IHttpClientFactory یک HttpClient را به ازای host درخواستی ایجاد کرده و در طول عمر برنامه از آن استفاده‌ی مجدد می‌کند. به همین جهت دیگر مشکل اشباع سوکت‌ها در این سیستم رخ نخواهند داد.


برای مطالعه‌ی بیشتر

You're using HttpClient wrong and it is destabilizing your software
Disposable, Finalizers, and HttpClient
Using HttpClient as it was intended (because you’re not)
Singleton HttpClient? Beware of this serious behaviour and how to fix it
Beware of the .NET HttpClient
Effectively Using HttpClient
مطالب
نحوه ایجاد یک نقشه‌ی سایت پویا با استفاده از قابلیت Reflection
طبق این استاندارد قالب نقشه‌ی سایت به فرم زیر می‌باشد:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
   <url>
      <loc>http://www.example.com/</loc>
      <lastmod>2005-01-01</lastmod>
      <changefreq>monthly</changefreq>
      <priority>0.8</priority>
   </url>
</urlset>

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

public enum ChangeFreq
        {
            Always,
            Hourly,
            Daily,
            Weekly,
            Monthly,
            Yearly,
            Never
        }

        [XmlElement("loc")]
        public string Url { get; set; }

        [XmlElement("lastmod")]
        public DateTime? LastModified { get; set; }
        public bool ShouldSerializeLastModified()
        {
            return LastModified.HasValue;
        }

        [XmlElement("changefreq")]
        public ChangeFreq? ChangeFrequency { get; set; }
        public bool ShouldSerializeChangeFrequency()
        {
            return ChangeFrequency.HasValue;
        }
        [XmlElement("priority")]
        public float? Priority { get; set; }
        public bool ShouldSerializePriority()
        {
            return Priority.HasValue;
        }
    }

دقت داشته باشید که چون پروپرتی‌های LastModified ، ChangeFrequency و Priority از نوع Nullable تعریف شده‌اند، پس باید کاری کنیم در صورتیکه این پروپرتی‌ها نال بودند سریالیز نشوند. بدین منظور از تابع ShouldSerialize[MemberName] استفاده می‌شود. این تابع  جزئی از دات نت است. کافی است بعد از ShouldSerialize نام پروپرتی را ذکر کنید. حال به کلاس دیگری نیاز داریم تا لیستی از کلاس فوق را دربر داشته باشد. 

[XmlRoot("urlset",Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
    public class SiteMp
    {
        private readonly List<Location> _locations;

        public SiteMp()
        {
            _locations = new List<Location>();
        }

        [XmlElement("url")]
        public List<Location> Locations
        {
            get { return _locations; }
            set
            {
                foreach (var location in value)
                {
                    Add(location);
                }
            } 
            
        }

        public void Add(Location location)
        {
            _locations.Add(location);
        }
    }

حال برای پردازش کلاس بالا لازم است ActionResultی را طراحی کنیم تا خروجی Response را به فرمت XML پردازش کند:

public class XmlResult : ActionResult
    {
        private readonly object _objectToSerialize;

        public XmlResult(object objectToSerialize)
        {
            _objectToSerialize = objectToSerialize;
        }
        public override void ExecuteResult(ControllerContext context)
        {
            if (_objectToSerialize == null)
               return;
             context.HttpContext.Response.Clear();
             var xmlSerializer = new XmlSerializer(_objectToSerialize.GetType());
             context.HttpContext.Response.ContentType = "text/xml";
             xmlSerializer.Serialize(context.HttpContext.Response.Output, _objectToSerialize);            
        }
    }

و در آخر یک کنترلر ساخته و به صورت زیر از آن استفاده می‌کنیم: 

public class SiteMapController : Controller
    {
        // GET: SiteMap
        public ActionResult Index()
        {
            SiteMp siteMap = new SiteMp();
            siteMap.Add(new Location
            {
                Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/Index"
            });
            siteMap.Add(new Location
            {
                Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/NewRequest",
                ChangeFrequency = Location.ChangeFreq.Always,
                LastModified = DateTime.UtcNow,
                Priority = 0.5f
            });
            siteMap.Add(new Location
            {
                Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/Home/FindRequest",
                ChangeFrequency = Location.ChangeFreq.Always,
                LastModified = DateTime.UtcNow,
                Priority = 0.5f
            });
            siteMap.Add(new Location
            {
                Url = Request.Url.GetLeftPart(UriPartial.Authority) + "/ContactUs/Index",
                ChangeFrequency = Location.ChangeFreq.Daily,
                LastModified = DateTime.UtcNow,
                Priority = 0.5f
            });
            return new XmlResult(siteMap);
        }

اگر دقت کنید لینک‌های ثابت باید به صورت دستی اضافه شوند. سناریویی را تصور کنید که لینک‌ها زیاد باشند(جدای از لینک هایی که از دیتابیس لود می‌شوند) این کار کمی ناجور به نظر می‌رسد. در اینجا میخواهیم از طریق امکانات ،Reflection عمل اضافه کردن لینک به صورت خودکار انجام شود. 

public class ControllerScanner
    {
       public static List<string> ScanAllControllers(HttpRequestBase requestBase)
        {
            Assembly asm = Assembly.GetAssembly(typeof(MvcApplication));

            var controllerActionlist = asm.GetTypes()
                .Where(type => typeof (Controller).IsAssignableFrom(type))
                .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
                .Where((returnType => returnType.ReturnType == (typeof(ViewResult)) || returnType.ReturnType==(typeof(ActionResult))))
                .Select(
                    x =>
                        new
                        {
                            Controller = x.DeclaringType.Name,
                            Action = x.Name,
                            ReturnType = x.ReturnType.Name

                        })
                .OrderBy(x => x.Controller).ThenBy(x => x.Action).Distinct().ToList();

            if (requestBase.Url == null)
                return null;

            var url = requestBase.Url.GetLeftPart(UriPartial.Authority);
            return controllerActionlist.Select(controller => $"{url}/{controller.Controller}/{controller.Action}").ToList();
        }
    }

حال از کلاس بالا در کنترلر SiteMap به صورت زیر استفاده می‌کنیم :

public class SiteMapController : Controller
    {
        // GET: SiteMap
        public ActionResult Index()
        {
            var siteMap = new SiteMap();
            var controllers = ControllerScanner.ScanAllControllers(Request);
            foreach (var controller in controllers)
            {
                siteMap.Add(new Location
                {
                    Url = controller,
                    ChangeFrequency = Location.ChangeFreq.Always,
                    LastModified = DateTime.UtcNow,
                    Priority = 0.5f
                });
            }
            return new XmlResult(siteMap);
        }        
    }

در آخر نیز سطر زیر را به سیستم مسیریابی اضافه نمایید تا در صورت درخواست فایل sitemap.xml  اکشن Index از کنترلر SiteMap فراخوانی شود.

 routes.MapRoute(
                "SiteMap", // Route name
                "sitemap.xml", // URL with parameters
                new { controller = "Sitemap", action = "Index", name = UrlParameter.Optional, area = "" }
            );


مطالب
استفاده از Flash Uploader در ASP.NET MVC
چندسال قبل یک کنترل آپلود فایل در برنامه‌های ASP.NET Web forms در سایت Code projects منتشر شد که من در چند پروژه از آن استفاده کردم.
در ادامه نحوه سازگار سازی این مجموعه را با ASP.NET MVC مرور خواهیم کرد:

الف) سورس‌های اصلی Flash کنترل ارسال فایل‌ها
اگر علاقمند به تغییر اطلاعاتی در فایل فلش نهایی هستید به پوشه OriginalFlashSource پروژه پیوست شده مراجعه کنید. در اینجا برای مثال یک سری از برچسب‌های آن فارسی شده‌اند و کامپایل مجدد.


ب) مزیت استفاده از Flash uploader
با استفاده از Flash uploader امکان انتخاب چندین فایل با هم وجود دارد. همچنین در صفحه دیالوگ انتخاب فایل‌ها دقیقا می‌توان پسوند فایل‌های مورد نظر را نیز تعیین کرد. این دو مورد در حالت ارسال معمولی فایل‌ها به سرور و استفاده از امکانات معمولی HTML وجود ندارند. به علاوه امکان نمایش درصد پیشرفت آپلود فایل‌ها و همچنین حذف کلی لیست و حذف یک آیتم از لیست را هم درنظر بگیرید.



ج) معادل کنترل Web forms را در ASP.NET MVC به شکل زیر می‌توان تهیه کرد:

@helper AddFlashUploader(
                string uploadUrl,
                string queryParameters,
                string flashUrl,
                int totalUploadSizeLimit = 0,
                int uploadFileSizeLimit = 0,
                string fileTypes = "",
                string fileTypeDescription = "",
                string onUploadComplete = "")
    {      
        onUploadComplete = string.IsNullOrEmpty(onUploadComplete) ? "" : "completeFunction=" + onUploadComplete;
        queryParameters = Server.UrlEncode(queryParameters);        
        fileTypes = string.IsNullOrEmpty(fileTypes) ? "" : "&fileTypes=" + Server.UrlEncode(fileTypes);
        fileTypeDescription = string.IsNullOrEmpty(fileTypeDescription) ? "" : "&fileTypeDescription=" + Server.UrlEncode(fileTypeDescription);
        var totalUploadSizeLimitData = totalUploadSizeLimit > 0 ? "&totalUploadSize=" + totalUploadSizeLimit : "";
        var uploadFileSizeLimitData = uploadFileSizeLimit > 0 ? "&fileSizeLimit=" + uploadFileSizeLimit : "";
        var flashVars = onUploadComplete + fileTypes + fileTypeDescription + totalUploadSizeLimitData + uploadFileSizeLimitData + "&uploadPage=" + uploadUrl + "?" + queryParameters;
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0"
        width="575" height="375" id="fileUpload" align="middle">
        <param name="allowScriptAccess" value="sameDomain" />
        <param name="movie" value="@flashUrl" />
        <param name="quality" value="high" />
        <param name="wmode" value="transparent">
        <param name=FlashVars value="@flashVars">
        <embed src="@flashUrl"
               FlashVars="@flashVars" 
               quality="high" 
               wmode="transparent" 
               width="575" 
               height="375" 
               name="fileUpload" 
               align="middle" 
               allowScriptAccess="sameDomain" 
               type="application/x-shockwave-flash" 
               pluginspage="http://www.macromedia.com/go/getflashplayer" />
    </object>                        
}
این اطلاعات در فایلی به نام FlashUploadHelper.cshtml در پوشه App_Code قرار خواهند گرفت.


د) نحوه استفاده از HTML helper فوق:

@{
    ViewBag.Title = "Index";
    var uploadUrl = Url.Action("Uploader", "Home");
    var flashUrl = Url.Content("~/Content/FlashUpload/FlashFileUpload.swf");
}
<h2>
    Flash Uploader</h2>
<div style="background: #E0EBEF;">
    @FlashUploadHelper.AddFlashUploader(
                uploadUrl: uploadUrl,
                queryParameters: "User=Vahid&Id=تست",
                flashUrl: flashUrl,
                fileTypeDescription: "Images",
                fileTypes: "*.gif; *.png; *.jpg; *.jpeg",
                uploadFileSizeLimit: 0,
                totalUploadSizeLimit: 0,
                onUploadComplete: "alert('انجام شد');")
</div>
با کدهای کنترلری معادل:
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.Mvc;

namespace MvcFlashUpload.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Uploader(string User, string Id, IEnumerable<HttpPostedFileBase> FileData)
        {
            var queryParameter1 = User;
            var queryParameter2 = Id;
            // ...

            foreach (var file in FileData)
            {
                if (file.ContentLength > 0)
                {
                    var fileName = Path.GetFileName(file.FileName);
                    var path = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
                    file.SaveAs(path);
                }
            }

            return Content(" ");
        }
    }
}

توضیحات:
در اینجا uploadUrl، مسیر اکشن متدی است که قرار است اطلاعات فایل‌ها را دریافت کند. queryParameters اختیاری است. اگر تعریف شود تعدادی کوئری استرینگ دلخواه را می‌تواند به متد Uploader ارسال کند. برای نمونه در اینجا User و Id ارسال شده‌اند یا هر نوع کوئری استرینگ دیگری که مدنظر است.
flashUrl مسیر فایل SWF را مشخص می‌کند. در اینجا فایل FlashFileUpload.swfدر پوشه Content/FlashUpload قرار گرفته است.
fileTypeDescription برچسبی است که نوع فایل‌های قابل انتخاب را به کاربر نمایش می‌دهد و fileTypes نوع‌های مجاز قابل ارسال را دقیقا مشخص می‌کند.
پارامترهای uploadFileSizeLimit و totalUploadSizeLimit در صورتیکه مساوی صفر وارد شوند، به معنای عدم محدودیت اندازه در فایل‌ها و جمع حجم ارسالی در هر بار است.
استفاده از پارامتر onUploadComplete اختیاری است. در اینجا می‌توان پس از پایان عملیات از طریق جاوا اسکریپت عملیاتی را انجام داد. برای مثال اگر خواستید کاربر را به صفحه خاصی هدایت کنید، window.locationرا مقدار دهی نمائید.
در متد Uploader کنترلر فوق، پارامترهای User و id اختیاری بوده و بر اساس queryParameters متد FlashUploadHelper.AddFlashUploader مشخص می‌شوند. اما نام FileData نباید تغییری کند؛ از این لحاظ که دقیقا همین نام در فایل فلش، مورد استفاده قرار گرفته است.
در اکشن متد دریافت فایل‌ها، لیستی از فایل‌های ارسالی به سرور دریافت شده و سپس بر این اساس می‌توان آن‌ها را در مکانی مشخص ذخیره نمود.


دریافت پروژه
MvcFlashUploader.zip
مطالب
مبانی TypeScript؛ متغیرها و نوع‌ها
روش‌های مختلف تعریف متغیرها در TypeScript

تمام توسعه دهنده‌های JavaScript با واژه‌ی کلیدی var آشنایی دارند؛ اما TypeScript واژه‌های کلیدی let و const را نیز اضافه کرده‌است (که جزئی از ES 6 نیز می‌باشند). تفاوت مهم بین var و let، در میدان دید متغیرهای تعریف شده‌ی توسط آن‌ها خلاصه می‌شود. پیشتر در سری مباحث بررسی ES 6، مطلب «متغیرها در ES 6» را نیز بررسی کردیم که در TypeScript نیز صادق می‌باشند؛ با این تفاوت که TypeScript را می‌توان به ES 5 نیز کامپایل کرد و بدون مشکل با تمام مرورگرهای موجود، اجرا نمود.
- متغیرهایی که با var تعریف می‌شوند، به صورت سراسری در متدی که تعریف شده‌اند، قابل دسترسی هستند؛ حتی اگر 5 سطح داخل ifهای تو در تو تعریف شده باشند. اما let و const تنها در block و قطعه‌ای که معرفی شده‌اند، معتبر بوده و خارج از آن تعریف نشده‌اند. در اینجا یک block توسط {} معرفی می‌شود.
- متغیرهای از نوع var به دلیل مفهومی به نام hoisting توسط runtime جاوا اسکریپت، به بالاترین سطح متد منتقل می‌شوند. به همین دلیل عمق تعریف آن‌ها، اثری در دسترسی به این متغیرها ندارد. اما hoisting در مورد let و const اعمال نمی‌شود.
- متغیرهای var را می‌توان چندبار مجددا تعریف کرد (هرچند این روش توصیه نمی‌شود؛ اما مجاز است). یک چنین تعریف مجددی با متغیرهای از نوع let و const مجاز نیست.
برای توضیحات بیشتر به مثال ذیل دقت کنید:
function ScopeTest() {
    if (true) {
        var foo = 'use anywhere';
        let bar = 'use in this block';
    }
    console.log(foo); // works!
    console.log(bar); // error!
}
در اینجا داخل قطعه‌ی if تعریف شده، یک متغیر، از نوع var و متغیر دیگری از نوع let تعریف شده‌است. خارج از بدنه‌ی این متد، متغیر foo هنوز قابل دسترسی است، اما متغیر bar خیر.


نوع‌های پایه‌ی TypeScript

نوع‌های پایه‌ی TypeScript شامل موارد ذیل هستند:
Boolean: برای ذخیره سازی true یا false.
let isDone: boolean = false;
Number: معرف مقادیر عددی اعشاری هستند.
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
String: مقادیر رشته‌ای را ذخیره می‌کند.
let name: string = "bob";
name = 'smith';
Array: روش‌های متفاوتی برای تعریف آرایه‌ها در TypeScript وجود دارند که در ادامه آن‌ها را بیشتر بررسی خواهیم کرد.
let list: number[] = [1, 2, 3];
Enum: نوع‌های شمارشی؛ جهت دادن نام‌هایی بهتر به مجموعه‌ی مشخصی از مقادیر.
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
Any: به این معنا که یک متغیر می‌تواند به هر نوع دلخواهی اشاره کند. هدف از وجود این نوع، امکان استفاده‌ی از کتابخانه‌های فعلی جاوا اسکریپتی است که نوعی برای متغیرهای آن‌ها تعریف نشده‌اند و در اساس هر نوعی می‌توانند باشند. برای مثال در جاوا اسکریپت مجاز است در سطر اول متغیری را تعریف کرد و در سطر دوم به آن یک رشته و در سطر سوم به آن یک عدد را انتساب داد.
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
Void: به معنای فقدان یک نوع است. برای مثال مشخص کردن اینکه متدی، خروجی ندارد.
 function warnUser(): void {
        alert("This is my warning message");
}

یک نکته: قابلیت Template string در ES 6 نیز در TypeScript پشتیبانی می‌شود.



مفهوم Type Inference در TypeScript

در TypeScript الزاما نیازی نیست تا نوع متغیری را صریحا مشخص کرد. در اینجا اگر نوع متغیری را در ابتدای کار تعریف نکنید، نوع آن در اولین باری که مقدار دهی می‌شود، مشخص خواهد شد که به آن Type Inference نیز می‌گویند.
let myString= 'this is a string';
myString= 42; // error!
در مثال فوق، نوع متغیر myString در زمان تعریف آن مشخص نشده‌است؛ اما با یک رشته، مقدار دهی و آغاز شده‌است. بر این اساس، TypeScript نوع این متغیر را رشته‌ای در نظر می‌گیرد و در سطر بعدی، از انتساب یک مقدار عددی به آن جلوگیری خواهد کرد.
و یا در مثال ذیل، نوع خروجی متد ReturnNumber به صورت صریح مشخص نشده‌است:
function ReturnNumber() {
   return 42;
}
let anotherString = 'this is also a string';
anotherString = ReturnNumber(); // error!
اما با توجه به اینکه خروجی آن عددی است، نوع آن نیز عددی درنظر گرفته شده و از انتساب آن به یک متغیر رشته‌ای جلوگیری می‌شود.


تعریف صریح نوع‌ها در TypeScript با استفاده از Type Annotations

برای لغو Type Inference و تعیین صریح نوع‌ها در TypeScript می‌توان به صورت زیر عمل کرد:
let myString : string = 'this is a string';
myString = 42; // error!

function ReturnNumber() : number {
   return 42;
}
let anotherString : string = 'this is also a string';
anotherString = ReturnNumber(); // error!
با قراردادن یک کولن پس از نام متغیر و سپس تعریف نوع داده‌ی مدنظر، می‌توان نوع‌ها را در TypeScript به صورت صریحی تعریف کرد. در مورد متدها نیز به همین صورت است. کولن پس از پایان بسته شدن پرانترها قرار می‌گیرد و سپس نوع بازگشتی متد مشخص می‌گردد.


نوع‌های شمارشی در TypeScript

Enums در بسیاری از زبان‌های برنامه نویسی متداول وجود دارند و هدف از آن‌ها، دادن نام‌هایی بهتر و مشخص، به مجموعه‌ای از مقادیر است:
 enum Category { Biography, Poetry, Fiction }; // 0, 1, 2
در مثال فوق یک enum جهت تعریف گروه‌های کتاب‌ها تعریف شده‌است. در اینجا بجای استفاده از مقادیر نامفهوم 0 تا 2، نام‌هایی مشخص و بهتر، جهت معرفی آن‌ها ارائه شده‌اند. به این ترتیب می‌توان در نهایت به کدهایی خواناتر رسید.
اگر می‌خواهید این مقادیر با اعداد دیگری شروع شوند (بجای صفر پیش فرض)، می‌توان مقدار اولین نام را به صورت صریحی مشخص کرد:
 enum Category { Biography = 1, Poetry, Fiction }; // 1, 2, 3
و یا الزامی به استفاده از مقادیر افزایش یابنده‌ای در اینجا نیست. در صورت نیاز می‌توان مقدار هر المان را جداگانه تعریف کرد:
 enum Category{ Biography = 5, Poetry = 8, Fiction = 9 }; // 5, 8, 9
پس از اینکه نوع enum تعریف شده، استفاده از آن همانند سایر نوع‌های پایه‌ی TypeScript است:
 let favoriteCategory: Category = Category.Biography;
در اینجا متغیر favoriteCategory از نوع enum گروه کتاب‌ها تعیین شده‌است؛ با مقدار اولیه‌ی Category.Biography.
در این حالت اگر مقدار favoriteCategory را چاپ کنیم، خروجی عددی 5 نمایش داده می‌شود:
 console.log(favoriteCategory); // 5
اگر نیاز به بازگشت مقدار رشته‌ای این آیتم است، می‌توان از ایندکس‌های یک enum استفاده کرد:
let categoryString= Category[favoriteCategory]; // Biography


آرایه‌ها در TypeScript

در حالت عمومی، آرایه‌ها در TypeScript همانند جاوا اسکریپت تعریف می‌شوند؛ البته به همراه تعدادی استثناء. در TypeScript سه روش برای تعریف آرایه‌ها وجود دارند:
الف) در مثال زیر آرایه‌ای از رشته‌ها تعریف شده‌است. در اینجا نوع آرایه به همراه [] مشخص می‌شود:
let strArray1: string[] = ['here', 'are', 'strings'];
ب) روش دوم تعریف آرایه‌ها در TypeScript با استفاده از مفهوم Generics است که در بحثی جداگانه به آن خواهیم پرداخت. در اینجا از واژه‌ی کلیدی Array به همراه مشخص سازی نوع آن در داخل <> استفاده شده‌است:
let strArray2: Array<string> = ['more', 'strings', 'here'];
ج) اگر نیاز به آرایه‌هایی شبیه به جاوا اسکریپت دارید که می‌توانند حاوی انواع و اقسام المان‌هایی با نوع‌های مختلف باشند، نوع آرایه را []any تعریف کنید:
let anyArray: any[] = [42, true, 'banana'];


نوع Tuples در TypeScript

Tuples در TypeScript نوع خاصی از آرایه‌ها هستند که نوع مقادیر اعضای آن‌ها به صورت صریح مشخص می‌شوند:
 let myTuple: [number, string] = [25, 'truck'];
برای مثال در اینجا نوع متغیر myTuple به صورت آرایه‌ای از دو عنصر عددی و رشته‌ای تعریف شده‌است.
اکنون برای دسترسی به مقادیر این المان‌ها، همانند کار با آرایه‌های معمولی، از ایندکس‌های آرایه‌ی تعریف شده استفاده می‌شود:
 let firstElement= myTuple[0]; // 25
let secondElement= myTuple[1]; // truck

یک نکته: می‌توان به آرایه‌ی تعریف شده، عناصر جدیدی را نیز افزود؛ با این شرط که نوع آن‌ها، یکی از نوع‌های مشخص شده‌ی در تعریف Tuple باشند:
 // other elements can have numbers or strings
myTuple[2] = 100;
myTuple[2] = 'this works!';


مفهوم Type assertions در TypeScript

حتما با مفهوم cast و تبدیل نوع‌های مختلف به یکدیگر، در زبان‌های دیگر برنامه نویسی آشنا هستید. در TypeScript نیز این مفهوم تحت عنوان Type assertions پشتیبانی می‌شود و دو روش برای تعریف آن وجود دارد:
الف) تعریف cast توسط angle-bracket syntax که در آن نوع مدنظر داخل یک <> قرار می‌گیرد:
 let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
در اینجا نوع نامشخص any به string تبدیل شده و سپس امکان دسترسی به خاصیت طول آن که تحت کنترل کامپایلر است و قابل انتساب به یک مقدار عددی، میسر شده‌است.
ب) تعریف cast توسط as syntax به نحو ذیل:
 let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
هر دو حالت تعریف شده معادل هستند. فقط باید دقت داشت در حین کار با ReactJS توسط TypeScript، فقط حالت as syntax پشتیبانی می‌شود.
اشتراک‌ها
مثال هایی از نحوه استفاده از JSON.stringify
JSON.stringify({});                  // '{}'
JSON.stringify(true);                // 'true'
JSON.stringify('foo');               // '"foo"'
JSON.stringify([1, 'false', false]); // '[1,"false",false]'
JSON.stringify({ x: 5 });            // '{"x":5}'

JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)) 
// '"2006-01-02T15:04:05.000Z"'

JSON.stringify({ x: 5, y: 6 });
// '{"x":5,"y":6}' or '{"y":6,"x":5}'
JSON.stringify([new Number(1), new String('false'), new Boolean(false)]);
// '[1,"false",false]'

JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }); 
// '{"x":[10,null,null,null]}' 
 
// Symbols:
JSON.stringify({ x: undefined, y: Object, z: Symbol('') });
// '{}'
JSON.stringify({ [Symbol('foo')]: 'foo' });
// '{}'
JSON.stringify({ [Symbol.for('foo')]: 'foo' }, [Symbol.for('foo')]);
// '{}'
JSON.stringify({ [Symbol.for('foo')]: 'foo' }, function(k, v) {
  if (typeof k === 'symbol') {
    return 'a symbol';
  }
});
// '{}'

// Non-enumerable properties:
JSON.stringify( Object.create(null, { x: { value: 'x', enumerable: false }, y: { value: 'y', enumerable: true } }) );
// '{"y":"y"}'
مثال هایی از نحوه استفاده از JSON.stringify
نظرات مطالب
Value Types ارجاعی در C# 7.2
یک نکته‌ی تکمیلی: اضافه شدن پارامترهای از نوع ref readonly به C# 12

در انتهای نکته‌ی خروجی ref readonly عنوان شد که «در ابتدا قصد داشتند ref readonly را برای تعریف پارامترهای value type نیز بکار برند، اما این تصمیم با معرفی پارامترهای از نوع in جایگزین شد» اما ... مجددا به C# 12 اضافه شده‌است:
مثال زیر را درنظر بگیرید:
namespace CS8Tests;

public class RefReadonlySample
{
   public void Test()
   {
      var number = 5;
      Print(ref number);
      Console.WriteLine($"After Print -> Your number is {number}");
      
      // Output:
      // Print -> Your number is 5
      // After Print -> Your number is 6
   }
   
   private void Print(ref int number)
   {
      Console.WriteLine($"Print -> Your number is {number}");
      number++;
   }
}
در این مثال، ارجاعی از متغیر عددی number (که یک value type است) به کمک واژه‌ی کلیدی ref به متد Print ارسال شده و درون این متد، مقدار این متغیر تغییر کرده‌است که این تغییر به خارج از متد Print نیز منعکس می‌شود.
اگر بخواهیم از تغییرات پارامتر number در متد Print جلوگیری کنیم، می‌توان از واژه‌ی کلیدی in که در C# 7.2 ارائه شد، استفاده کرد:
 private void Print(in int number)
در این حالت در سطر ++number، به خطای زیر می‌رسیم:
error CS8331: Cannot assign to variable 'number' or use it as the right hand side of a ref assignment because it is a readonly variable

اکنون در C# 12 همین عمل را توسط واژه‌های کلیدی ref readonly نیز می‌توان پیاده سازی کرد:
private void Print(ref readonly int number)
خطایی را هم که گزارش می‌دهد، دقیقا همانند خطای ذکر شده‌ی واژه‌ی کلیدی in است.

سؤال: چرا این تغییر در C# 12 رخ داده‌است، زمانیکه واژه‌ی کلیدی in، دقیقا همین کار را انجام می‌داد؟
هدف، وضوح بیشتر API تولیدی و تاکید بر readonly بودن ارجاع دریافتی در این حالت و یکدستی قسمت‌های مختلف زبان است.
همچنین واقعیت این است که یک چنین قابلیت‌هایی، استفاده‌ی روزمره‌ای را در زبان #‍C ندارند و بیشتر هدف از وجود آن‌ها، استفاده از API کتابخانه‌های C++/C در زبان #C است. برای مثال بجای اینکه تمام ارجاعات فقط خواندنی آن‌ها را به پارامترهایی از نوع in تبدیل کنند (در کدهای قدیمی) که سبب بروز مشکلات عدم سازگاری می‌شود، اکنون می‌توانند به سادگی refهای قدیمی تعریف شده را ref readonly کنند؛ بدون اینکه استفاده کنندگان با مشکلی مواجه شوند.