درس دوم – عبارات، انواع و متغیرها در C#
در این درس به معرفی عبارات، انواع و متغیرها در زبان C# میپردازیم. هدف از این درس بررسی موارد زیر است :
متغیرها، به بیان بسیار ساده، مکانهایی جهت ذخیره اطلاعات هستند. شما اطلاعاتی را در یک متغیر قرار میدهید و از این اطلاعات بوسیله متغیر در عبارات C# استفاده مینمایید. کنترل نوع اطلاعات ذخیره شده در متغیرها بوسیله تعیین کردن نوع برای هر متغیر صورت میپذیرد.
C# زبانی بسیار وابسته به انواع است، بطوریکه تمامی عملیاتی که بر روی دادهها و متغیرها در این زبان انجام میگیرد با دانستن نوع آن متغیر میسر میباشد. قوانینی نیز برای تعیین اینکه چه عملیاتی بر روی چه متغیری انجام شود نیز وجود دارد.(بسته به نوع متغیر)
انوع ابتدایی زبان C# شامل : یک نوع منطقی(Boolean) و سه نوع عددی اعداد صحیح(integer)، اعداد اعشاری(Floating points) و اعداد دسیمال(Decimal) میباشد.(به انواع Boolean از اینرو منطقی میگوییم که تنها دارای دو حالت منطقی صحیح(True) و یا غلط(False) میباشند.)
مثال 1 – نشان دادن مقادیر منطقی (Boolean)
using System;
class Booleans
{
public static void Main()
{
bool content = true;
bool noContent = false;
Console.WriteLine("It is {0} that C# Persian provides C# programming language
content.", content);
Console.WriteLine("The statement above is not {0}.", noContent);
}
}
در این مثال، مقادیر منطقی متغیرهای Boolean به عنوان قسمتی از جمله در خروجی نمایش داده میشوند. متغیرهای bool تنها میتوانند یکی از دو مقدار true یا false را داشته باشند، یعنی همانند برخی از زبانهای برنامهسازی مشابه، مانند C و یا C++، مقدار عددی نمیپذیرند، زیرا همانگونه که میدانید در این دو زبان هر مقدار عددی صحیح مثبت بغیر از صفر به عنوان true و عدد صفر به عنوان false در نظر گرفته میشود و در حقیقت نوع bool در این دو زبان نوعی integer میباشند. اما در زبان C# انواع bool یکی از دو مقدار true یا false را میپذیرند. خروجی برنامه بالا به صورت زیر است :
It is True that C# Persian provides C# programming language content.
The statement above is not False.
جدول زیر تمامی انواع عددی صحیح C#، اندازه آنها و رنج قابل قبول آنها را نشان میدهد.
رنج قابل قبول
اندازه به بیت
نوع
128- تا 127
8
sbyte
0 تا 255
8
byte
32768- تا 32767
16
short
0 تا 65535
16
ushort
2147483648- تا 2147483647
32
int
0 تا 4294967295
32
uint
9223372036854775808- تا 9223372036854775807
64
long
0 تا 18446744073709551615
64
ulong
از این انواع برای محاسبات عددی استفاده میگردد. یک نوع دیگر را نیز میتوان در این جدول اضافه نمود و آن نوع char است. هر چند شاید از نظر بسیاری از دوستانی که با زبانهای دیگر برنامهسازی کار کردهاند این تقسیم بندی غلط به نظر آید، اما باید گفت که در زبان C# نوع char نیز نوع خاصی از انواع عددی است که رنجی بین صفر تا 65535 دارد و اندازه آن نیز 16 بیتی است، اما به جای نمایش دادن مقادیر عددی تنها میتواند بیان کننده یک کاراکتر باشد. در آینده در این مورد بیشتر توضیح خواهم داد.
جدول زیر تمامی انواع عددی اعشاری زبان C# را نمایش میدهد.
رنج قابل قبول
دقت
اندازه به بیت
نوع
تا
7 رقم
32
float
تا
15-16 رقم
64
double
تا
28-29 رقم دسیمال
128
decimal
انواعی از نوع floating point هنگامی استفاده میشوند که محاسبات عددی به دقتهای اعشاری نیاز داشته باشند. همچنین برای منظورهای تجاری استفاده از نوع decimal بهترین گزینه است. این نوع تنها در زبان C# وجود دارد و در زبانهای مشابه به آن نظیر Java چنین نوعی در نظر گرفته نشده است.
در یک زبان برنامهسازی نتایج بوسیله ایجاد یک سری عبارت تولید میگردند. عبارات از ترکیب متغیرها و عملگرها در دستورالعملهای یک زبان ایجاد میگردند.(توجه نمایید که عبارت معادل expression و دستورالعمل معادل statement میباشد که ایندو با یکدیگر متفاوت میباشند.) جدول زیر عملگرهای موجود در زبان C#، حق تقدم آنها و شرکتپذیری آنها را نشان میدهد.
شرکتپذیری
عملگر(ها)
نوع عمل
از چپ
(x) x.y f(x) a[x] x++ x--
new typeof sizeof checked unchecked
عملیات ابتدایی
از چپ
+ - ! ~ ++x --x (T)x
عملیات یکانی
از چپ
* / %
عملیات ضربی
از چپ
- +
عملیات جمعی
از چپ
<< >>
عمل شیفت
از چپ
< > <= >= is
عملیات رابطهای
از راست
== !=
عملیات تساوی
از چپ
&
عمل AND منطقی
از چپ
|
عمل OR منطقی
از چپ
^
عمل XOR منطقی
از چپ
&&
عمل AND شرطی
از چپ
||
عمل OR شرطی
از چپ
?:
عمل شرطی
از راست
= *= /= %= += -= <<= >>= &= ^= |=
عمل انتساب
شرکتپذیری از چپ بدین معناست که عملیات از چپ به راست محاسبه میشوند. شرکتپذیری از راست بدین معناست که تمامی محاسبات از راست به چپ صورت میگیرند. به عنوان مثال در یک عمل تساوی، ابتدا عبارات سمت راست تساوی محاسبه شده و سپس نتیجه به متغیر سمت چپ تساوی تخصیص داده میشود.
مثال 2- عملگرهای یکانی (Unary)
using System;
class Unary
{
public static void Main()
{
int unary = 0;
int preIncrement;
int preDecrement;
int postIncrement;
int postDecrement;
int positive;
int negative;
sbyte bitNot;
bool logNot;
preIncrement = ++unary;
Console.WriteLine("Pre-Increment: {0}", preIncrement);
preDecrement = --unary;
Console.WriteLine("Pre-Decrement: {0}", preDecrement);
postDecrement = unary--;
Console.WriteLine("Post-Decrement: {0}", postDecrement);
postIncrement = unary++;
Console.WriteLine("Post-Increment: {0}", postIncrement);
Console.WriteLine("Final Value of Unary: {0}", unary);
positive = -postIncrement;
Console.WriteLine("Positive: {0}", positive);
negative = +postIncrement;
Console.WriteLine("Negative: {0}", negative);
bitNot = 0;
bitNot = (sbyte)(~bitNot);
Console.WriteLine("Bitwise Not: {0}", bitNot);
logNot = false;
logNot = !logNot;
Console.WriteLine("Logical Not: {0}", logNot);
}
}
به هنگام محاسبه عبارات، دو عملگر x++ و x—(که در اینجا کاراکتر x بیان کننده آن است که عملگرهای ++ و – در جلوی عملوند قرار میگیرند post-increment و post-decrement) ابتدا مقدار فعلی عملوند (operand) خود را باز میگرداند و سپس به عملوند خود یک واحد اضافه کرده یا از آن یک واحد میکاهند. عملگر ++ یک واحد به عملوند خود اضافه میکند و عملگر – یک واحد از عملوند خود میکاهد. بدین ترتیب عبارت x++ معادل است با عبارت x=x+1 و یا x+=1 اما همانطور که گفته شد باید توجه داشته باشید که این عملگرها(++ و --) ابتدا مقدار فعلی عملوند خود را برگشت میدهند و سپس عمل خود را روی آنها انجام میدهند. بدین معنی که در عبارت x=y++ در صورتیکه در ابتدای اجرای برنامه مقدار x=0 و y=1 باشد، در اولین اجرای برنامه مقدار x برابر با 1 یعنی مقدار y میشود و سپس به متغیر y یک واحد افزوده میشود، در صورتیکه اگر این عبارت را بصورت x=++y بنویسیم در اولین اجرای برنامه، ابتدا به مقدار متغیر y یک واحد افزوده میشود و سپس این مقدار به متغیر x تخصیص داده میشود که در این حالت مقدار متغیر x برابر با 2 میشود.(در مورد عملگر – نیز چنین است.) پس با این توضیح میتوان گفت که دو عملگر ++x و –x ابتدا به عملوند خود یک واحد اضافه یا یک واحد از آن کم میکنند و سپس مقدار آنها را باز میگردانند.
در مثال 2، مقدار متغیر unary در قسمت اعلان برابر با 0 قرار گرفته است. هنگامیکه از عملگر ++x استفاده میکنیم، به مقدار متغیر unary یک واحد افزوده میشود و مقدارش برابر با 1 میگردد و سپس این مقدار، یعنی 1، به متغیر preIncrement تخصیص داده میشود. عملگر –x مقدار متغیر unary را به 0 باز میگرداند و سپس این مقدار را به متغیر preDecrement نسبت میدهد.
هنگامیکه از عملگر x-- استفاده میشود، مقدار متغیر unary، یا همان مقدار صفر، به متغیر postDecrement تخصیص داده میشود و سپس از مقدار متغیر unary یک واحد کم شده و مقدار این متغیر به 1- تغییر میکند. سپس عملگر x++ مقدار متغیر unary، یعنی همان 1-، را به متغیر postIncrement تخصیص میدهد و سپس یک واحد به مقدار متغیر unary میافزاید تا مقدار این متغیر برابر با 0 (صفر) شود.
مقدار متغیر bitNot در هنگام اعلان برابر با صفر است. با استفاده از عملگر نقیض بیتی (~) (یا عملگر مکملگیری) متغیر bitNot بعنوان یک بایت در نظر گرفته میشود و مقدار آن منفی یا نقیض میشود. در عملیات بیتی نقیض بدین معناست که تمامی یکها به صفر و تمامی صفرها به یک تبدیل شوند. در این حالت نمایش باینری عدد صفر یا همان 00000000 به نقیض آن یعنی 11111111 تبدیل میگردد.
در این مثال به عبارت (sbyte)(~bitNot) توجه نمایید. هر عملی که بر روی انواع short،unshort ، byte و sbyte انجام شود، مقداری از نوع int را باز میگرداند. بمنظور اینکه بتوانیم نتیجه دلخواه را به متغیر bitNot تخصیص دهیم باید از فرمت (Type) operator استفاده نماییم که در آن Type نوعی است میخواهیم نتیجه ما به آن نوع تبدیل شود و operator عملی است که بر روی متغیر صورت میپذیرد. به بیان دیگر چون میخواهیم مقدار متغیر bitNot بصورت بیتی در نظر گرفته شود، پس باید نتیجه عمل ما بصورت بیتی در آن ذخیره شود که استفاده از نوع sbyte باعث میشود تا نتیجه به فرم بیتی (یا بایتی) در متغیر ما ذخیره شود. باید توجه نمایید که استفاده از فرمت (Type) یا در اصطلاح عمل Casting، در مواقعی که میخواهیم تغییری از یک نوع بزرگتر به نوع کوچکتر ایجاد نماییم، مورد استفاده قرار گیرد، چرا که در این حالات ممکن است با از دست دادن اطلاعات مواجه باشیم. در این مثال چون میخواهیم نوع بزرگتر int را به(32 بیتی) به نوع کوچکتر sbyte (8 بیتی) تبدیل نماییم، بدین منظور باید بطور صریح از عمل Casting استفاده نماییم تا اطلاعاتی در این تبدیل از بین نرود. در مورد تبدیل انواع کوچکتر به انواع بزرگتر(مثلا تبدیل sbyte به int) نیازی به استفاده از عمل Casting نیست چرا که امکان از بین رفتن اطلاعات وجود ندارد. در ضمن باید به یک نکته مهم توجه نمایید و آن تبدیل انواع علامتدار(Signed) و بدون علامت(Unsigned) به یکدیگر است. در این حالت خطر بسیار مهمی دادههای شما را تهدید مینماید. بحث در مورد مسائل پیچیدهتر در مورد تبدیل انواع علامتدار و و بدون علامت به یکدیگر در اینجا نمیگنجد و سعی میکنم تا آنها را در مطالب بعدی و در جای لازم مورد بحث و بررسی قرار دهم.(در صورتیکه برخی از مطالب این قسمتها برای شما به خوبی قابل درک نیست، نگران نباشید چراکه در آینده در مثالهایی که خواهید دید تمامی این مطالب را در عمل نیز حس کرده و با آنها آشنا خواهید شد.)
عملگر بعدی که در این برنامه مورد استفاده قرار گرفته است، عملگر نقیض منطقی یا همان "!" است که امکان تغییر مقدار یک متغیر Boolean را از true به false و بالعکس را فراهم میآورد. در مثال بالا(مثال شماره 2) مقدار متغیر logNot پس از استفاده از عملگر "!" از false به true تغییر کرده است. با توجه به توضیحات اخیر خروجی زیر از برنامه مثال 2 مورد انتظار است :
Pre-Increment: 1
Pre-Decrement 0
Post-Decrement: 0
Post-Increment -1
Final Value of Unary: 0
Positive: 1
Negative: -1
Bitwise Not: -1
Logical Not: True
مثال 3 – عملگرهای دوتایی
using System;
class Binary
{
public static void Main()
{
int x, y, result;
float floatResult;
x = 7;
y = 5;
result = x+y;
Console.WriteLine("x+y: {0}", result);
result = x-y;
Console.WriteLine("x-y: {0}", result);
result = x*y;
Console.WriteLine("x*y: {0}", result);
result = x/y;
Console.WriteLine("x/y: {0}", result);
floatResult = (float)x/(float)y;
Console.WriteLine("x/y: {0}", floatResult);
result = x%y;
Console.WriteLine("x%y: {0}", result);
result += x;
Console.WriteLine("result+=x: {0}", result);
}
}
خروجی این برنامه به فرم زیر است :
x+y: 12
x-y: 2
x*y: 35
x/y: 1
x/y: 1.4
x%y: 2
result+=x: 9
مثال 3 استفادههای متفاوتی از عملگرهای دوتایی را نشان میدهد.(منظور از عملگر دوتایی، عملگری است که دارای دو عملوند میباشد مانند عملگر جمع "+"). بسیاری از عملگرهای مورد استفاده در این مثال عملگرهای ریاضی هستند و نتیجه عمل آنها مشابه عملی است که از آنها در ریاضیات دیدهاید. از نمونه این عملگرها میتوان به عملگرهای جمع "+"، تفریق "-"، ضرب "*" و تقسیم "/" اشاره نمود.
متغیر floatResult از نوع اعشاری یا float تعریف شده است. در این مثال نیز صریحاً از عمل Casting جهت اسفاده از دو متغیر x و y که از نوع int هستند، برای انجام عملی که نتیجهاش از نوع float است، استفاده کردهایم.
در این مثال از عملگر "%" نیز استفاده کردهایم. این عملگر در عملیات تقسیم کاربرد دارد و باقیمانده تقسیم را برمیگرداند. یعنی دو عملوند خود را بر یکدیگر تقسیم میکند و باقیمانده این تقسیم را برمیگرداند.
در این مثال همچنین فرم جدیدی از عمل انتساب را بصورت result+=x مشاهده مینمایید. استفاده از عملگرهای انتسابی که خود ترکیبی از دو عملگر هستند، جهت سهولت در امر برنامهنویسی مورد استفاده قرار میگیرند. عبارت فوق معادل result = result+x میباشد. یعنی مقدار قبلی متغیر result با مقدار متغیر x جمع میشود و نتیجه در متغیر result قرار میگیرد.
یکی دیگر از انواعی که تا کنون با آن سر و کار داشتهایم نوع رشتهای (string) است. یک رشته، از قرار گرفتن تعدادی کاراکتر در کنار یکدیگر که داخل یک زوج کوتیشن " " قرار گرفتهاند، ایجاد میگردد. بعنوان مثال "Hi This is a string type". در اعلان متغیرها نیز در صورت تعریف متغیری از نوع رشتهای، در صورت نیاز به تخصیص مقدار به آن، حتماً کاراکترهایی که میخواهیم بعنوان یک رشته به متغیرمان نسبت دهیم را باید داخل یک زوج کوتیشن " " قرار دهیم. به مثال زیر توجه نمایید.
string Name;
…
Name = "My name is Hadi";
همانطور که در این مثال مشاهده مینمایید، متغیری از نوع رشتهای تحت نام Name تعریف شده است و سپس در جایی از برنامه که نیاز به تخصیص مقدار برای این متغیر وجود دارد، عبارت مورد نظر را داخل دو کوتیشن قرار داده و به متغیر خود تخصیص دادهایم. رشتهها از پر کاربرد ترین انواع در زبانهای برنامهسازی جهت ایجاد ارتباط با کاربر و دریافت اطلاعات از کاربر میباشند.(همانطور که در درس قبل اول نیز گفته شد، دستور Console.ReadLine() یک رشته را از ورودی دریافت مینماید.) در مثالهایی که در طی درسهای این سایت خواهید دید، نمونههای بسیاری از کاربرد انواع مختلف و نیز نوع رشتهای را خواهید دید.