PDA

View Full Version : مقاله : C# 2008 Language Features - Implicit Data Typing



hdv212
پنج شنبه 08 فروردین 1387, 14:00 عصر
خب C# 2008 هم بالاخره اومد و جای خودش رو کم و بیش در بین برنامه نویسان باز کرد، ولی این نسخه قابلیتهای بسیار زیادی نسبت به نسخه های قبلی داره که در طی مقالاتی به تشریح هر قسمت خواهیم پرداخت، لازم به ذکره که این مجموعه مقالات، ترجمه ای از کتاب Pro C# 2008 and the NET 3.5 Platform Fourth Edition از انتشارات Apress هست و من تا اونجایی که ممکنه سعی میکنم به صورت سلیس و روان ترجمه کنم، ولی از از ترجمه گذشته، پیشنهاد میکنم حتما این کتاب رو بخونید چون هم کامله و هم روان و پر از مثال های متعدد که به زودی در بطن مقاله خواهید دید، خب سخن رو کم میکنم و شروع به بررسی قابلیتهای جدید زبان سی شارپ میکنم :
قابلیتهایی رو ما در طی این سلسله مقالات بررسی میکنیم عبارتند از :
Implicit Data Typing
Automatic Properties
Extension Methods
Partial Methods
Object Initializers
Anonymous Types
با وجود اینکه این قابلیتها به ساخت برنامه های انعطلاف پذیر تر و قدرتمندتر کمک میکنه، ولی مزیت استفاده از امکانات فوق زمانی آشکار میشه که بخواهید با تکنولوژی قدرتمند Linq کار کنید، پس اگه مزیتهای استفاده از این امکانات جدید براتون مبهم بود، دلسرد نشید و بدونید که خیلی از اینها در زمان کار با Linq حسابی به د ردتون میخورن.
اگه عمری باقی باشه و وقت کنم، سعی میکنم بخش مربوط به Linq رو هم ترجمه کنم، و اما بخش اول :
مفهوم Implicit Data Typing :
اجازه بدید بیان این موضوع را با ساخت یک پروژه از نوع Console Application به نام ImplicitlyTypedLocalVars شروع کنیم، همانطور که تا حالا میدونستید و برنامه نویسی میکردید، شما Local Variables رو به صورت صریح ) (Explicitو قابل پیشبینی تعریف میکردید :

static void DeclareExplicitVars()
{
// Explicitly typed local variables
// are declared as follows:
// dataType variableName = initialValue;
int myInt = 0;
bool myBool = true;
string myString = "Time, marches on...";
}
ولی C# 2008 کلمه ی کلیدی جدیدی رو ارائه کرده به نام var که شما میتونید در جایی که میخواهید متغیر محلی (مانند انواعی مثل int,bool یا string) رو تعریف کنید، از اون استفاده کنید، وقتی این کار رو کردید، کامپایلر از طریق مقدار اولیه ای که به متغیر مورد نظر نسبت دادید، نوع اون رو حدس میزنه، برای مثال، متغیرهای مثال قبل، الان میتونن اینطوری اعلان بشن :

static void DeclareImplicitVars()
{
// Implicitly typed local variables
// are declared as follows:
// var variableName = initialValue;
var myInt = 0;
var myBool = true;
var myString = "Time, marches on...";
}
توجه : var در حقیقت یک کلمه ی کلیدی در سی شارپ نیست، این کلمه به شما اجازه میده که variables یا parameters یا fields رو علامت دار کنید تا بدون خطای زمان کامپایل (Compile-Time)، کامپایل بشن. به هر حال وقتی از علامت var به عنوان Data Type استفاده میشه، کامپایلر به عنوان یک Keyword باهاش رفتار میکنه.
خب در مثال بالا، کامپایلر میتونه حدس بزنه که نوع متغیر myInt در حقیقت System.Int32 هست، نوع متغیر myBool در واقع System.Boolean هست و بالاخره نوع myString همون System.String هست (بر اساس مقادیر اولیه ای که بهشون نسبت داده شده)، برای درک بهتر، میتونید این موضوع رو با استفاده از Reflection بررسی کنید :

static void DeclareImplicitVars()
{
// Implicitly typed local variables.
var myInt = 0;
var myBool = true;
var myString = "Time, marches on...";
// Print out the underlying type.
Console.WriteLine("myInt is a: {0}", myInt.GetType().Name);
Console.WriteLine("myBool is a: {0}", myBool.GetType().Name);
Console.WriteLine("myString is a: {0}", myString.GetType().Name);
}
توجه کنید که شما میتونید Implicit Data Typing رو برای هر نوع داده ای مثل داده هایی از نوع arrays,generic و یا حتی type های تعریف شده ی خودتون استفاده کنید :

static void DeclareImplicitVars()
{
...
// More implicitly typed local variables.
var evenNumbers = new int[] { 2, 4, 6, 8 };
var myMinivans = new List<MiniVan>();
var myCar = new SportsCar();
Console.WriteLine("evenNumbers is a: {0}", evenNumbers.GetType().Name);
Console.WriteLine("myMinivans is a: {0}", myMinivans.GetType().Name);
Console.WriteLine("myCar is a: {0}", myCar.GetType().Name);
}
استفاده از var در داخل حلقه ی foreach :
شما میتونید از قابلیت Implicit Data Typing در داخل یک حلقه ی foreach هم استفاده کنید، همانطور که انتظار دارید، کامپایلر نوع صحیح متغیر مورد نظر رو به درستی حدس میزنه (type of type). این متد رو ملاحظه کنید که توسط حلقه ی foreach روی هر یک از عناصر یک آرایه ای محلی از نوع int که به صورت Implicit تعریف شده پیمایش میکنه :

static void VarInForeachLoop()
{
var evenNumbers = new int[] { 2, 4, 6, 8 };
// Use "var" in a standard foreach loop.
foreach (var item in evenNumbers)
{
Console.WriteLine("Item value: {0}", item);
}
}
بهرحال، یک حلقه ی foreach باعث استفاده از پیمایش قویا تعیین نوع شده (Strongly Typed Iterator) در هنگام پردازش یک آرایه ای که به صورت Implicit تعریف شده میشه، بنابراین، این کد هم از لحاظ قواعد نحوی (Syntactically) درسته :

static void VarInForeachLoop()
{
var evenNumbers = new int[] { 2, 4, 6, 8 };
// Use a strongly typed System.Int32 to iterate over contents.
foreach (int item in evenNumbers)
{
Console.WriteLine("Item value: {0}", item);
}
}
محدودیتهای متغیرهایی که به صورت Implicit تعریف میشوند :
البته محدودیتهایی هم در استفاده از کلمه ی کلیدی var هم وجود دارد. اول اینکه تعیین نوع ضمنی (Implicit Typing) فقط بر روی متغیرهایی محلی که در محدوده ی یک Method یا Property تعریف شدند اعمال خواهد شد، همچنین شما مجاز نیستید که از کلمه ی کلیدی var برای مقادیر یا پارامترهای بازگشتی استفاده کنید :

class ThisWillNeverCompile
{
// Error! var cannot be used as field data!
private var myInt = 10;
// Error! var cannot be used as a return value
// or parameter type!
public var MyMethod(var x, var y){}
}
مورد دیگه اینه که وقتی از کلمه ی کلیدی var استفاده میکنید، بایستی حتما مقدار اولیه ی متغیر رو در همان زمان اعلانش مشخص کنید، همچنین از مقدار null هم نمیتونید برای مقدار دهی به متغیر مورد نظر استفاده کنید. اولین محدودیت باعث میشه که عمل تعریف یک متغیر به صورت Implicitly Typing شبیه پروسه ی تعریف یک ثابت به وسیله ی کلمه ی کلیدی const به نظر بیاد. این آخرین محدودیت بایستی بفهمه، ولی در واقع کامپایلر نمیتونه حدس بزنه، متغیر به چه گونه ای از type در حافظه اشاره کنه، فقط روی null :

// Error! Must assign a value!
var myData;
// Error! Must assign value at exact time of declaration!
var myInt;
myInt = 0;
// Error! Can't assign null as initial value!
var myObj = null;
بهر حال مجاز هستید بعد از اینکه مقدار اولیه رو تنظیم کردید، از null استفاده کنید (برای انواع ارجاعی یا reference type) :
// OK, is SportsCar is a reference type!
var myCar = new SportsCar();
myCar = null;
بعلاوه، شما مجاز هستید که متغیری که به صورت Implicitly Typing تعریف کردید، به متغیر های دیگه، اعمال کنید، یا Implicitly Typed باشن یا نباشند :

// Also OK!
var myInt = 0;
var anotherInt = myInt;
string myString = "Wake up!";
var myData = myString;
همچنین شما مجاز هستید که یک مقدار Implicitly Typed رو به فراخوانی کننده (caller) برگردونید، این کار به وسیله ی مقدار بازگشتی متدی که مطابق با نوع پشت پرده ی (underlying type) متغیر تعریف شده با var هست صورت میگیرد :

static int GetAnInt()
{
var retVal = 9;
return retVal;
}
Implicitly Typed Local Arrays
یکی از موضوعاتی که به بحث ما خیلی نزدیکه، Implicitly Typed Local Arrays است. با استفاده از ان تکنیک، شما میتونید یک آرایه ی جدید رو بدون اینکه نوع عناصر درون اون رو تعیین کنید، اختصاص بدید :

static void DeclareImplicitArrays()
{
// a is really int[].
var a = new[] { 1, 10, 100, 1000 };
Console.WriteLine("a is a: {0}", a.ToString());
// b is really double[].
var b = new[] { 1, 1.5, 2, 2.5 };
Console.WriteLine("b is a: {0}", b.ToString());
// c is really string[].
var c = new[] { "hello", null, "world" };
Console.WriteLine("c is a: {0}", c.ToString());
// myCars is really SportsCar[].
var myCars = new[] { new SportsCar(), new SportsCar() };
Console.WriteLine("myCars is a: {0}", myCars.ToString());
Console.WriteLine();
}
صد البته وقتی که شما یک آرایه رو با استفاده از دستورات C# به صورت صریح (explicit) تخصیص میدید، آیتمهای اون آرایه ی Initialize شده باید مطابق با نوع پشت پرده (Underlying) باشند(همه باید یا int باشند یا string باشند یا SportCar یا ...).
برخلاف اون چیزی که انتظارش رو دارید، یک آرایه ی محلی که به صورت ضمنی (Implicit) تعریف شده، به صورت پیش فرض، System.Object نیست، بنا براین، این کد ایجاد خطای زمان کامپایل میکند :

// Error! Mixed types!
var d = new[] { 1, "one", 2, "two", false };
داده های تعیین نوع شده به صورت ضمنی (Implicit Typed)، داده های قویا تعیین نوع شده هستند(Strongly Typed Data)
حواستون باشه که متغیرهایی که به صورت Implicit تعیین نوع میشن، نتیجه ش به صورت Strongly Typed Data است(قویا تعیین نوع شده هستند). بنابراین، استفاده از کلمه ی کلیدی var با تکنیک استفاده شده در زبانهای اسکریپتی (مثل VBScript یا Perl) یا نوع Com Variant یکی نیست، جایی که یک متغیر بتونه مقادیری رو از انواع مختلف بر چرخه ی عمر خودش نگه داره (بسته به شرایط Dynamic Typing میگن).
ترجیحا، نوع استنباطی، ظاهر Strongly Typed زبان سی شارپ رو نگه میداره و فقط بر اعلان متغیرها در زمان کامپایل تاثیر میذاره. بعد از اون موضوع، با اون متغیر اینطوری رفتار میشه که اگر با اون نوع اعلان شده بود، انتساب مقداری از نوع دیگه به اون متغیر، باعث ایجاد خطای زمان کامپایل میشود :

static void ImplicitTypingIsStrongTyping()
{
// The compiler knows "s" is a System.String.
var s = "This variable can only hold string data!";
s = "This is fine...";
// Can invoke any member of the underlying type.
string upper = s.ToUpper();
// Error! Can't assign numerical data to a a string!
s = 44;
}
سودمندی متغیرهایی که به صورت Implicitly Typed تعریف شدند
الان که با syntax مورد استفاده برای اعلان متغیرها به صورت Implicitly Typed آشنا شدید، مطمئن هستم که این سوال براتون بوجود اومده که کجا میتونید از این تکنیک استفاده کنید. در درجه ی اول، استفاده ی بی رویه از کلمه ی var خوانندگانی که کد شما رو میخونن رو کمی گیج کنه، و به سختی میتونن به سرعت نوع پشت پرده ی (Underlying) متغیر مورد نظر رو تشخیص بدن، پس بنابراین، اگر شما واقعا به یک متغیر از نوع int نیاز دارید، اون رو به صورت int اعلان کنید.
بهرحال در تکنولوژی Linq که از Query Expressions استفاده میکنیم، اون میتونه نتیجه رو به صورت Dynamic که مبتنی بر فرمت خود query هست رو برگردونه، در اینجا، استفاده از انواع ضمنی (Implicit Typing) بی نهایت مفیده، بعنوان مثال ما نیازی نیست که نوع نتیجه ای رو که query ممکنه برگردونه به صورت صریح (Explicit) تعریف کنیم که در برخی موارد حتی غیر ممکنه :

static void QueryOverInts()
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
var subset = from i in numbers where i < 10 select i;
Console.Write("Values in subset: ");
foreach (var i in subset)
{
Console.Write("{0} ", i);
}
Console.WriteLine();
// Hmm...what type is subset?
Console.WriteLine("subset is a: {0}", subset.GetType().Name);
Console.WriteLine("subset is defined in: {0}", subset.GetType().Namespace);
}
بهرحال این میتونه مثالی باشه در استفاده از var برای تعریف داده ای که برگردانده میشه از یک Linq Query.

نمونه کد مقاله ی بالا رو میتونید از اینجا دانلود کنید :