پست 2 : دسترسی به کاراکترها و طول رشته
هدف: یادگیری دسترسی به کاراکترهای خاص و محاسبه طول رشته.
محتوا: رشته از تعدادی کاراکتر تشکیل شده که میتوانید به کاراکترهای خاص یک رشته دسترسی پیدا کنید یا تعداد کاراکترهای آن را بشمارید.
دسترسی به کاراکترها:
با استفاده از اندیس ([]) میتوانید یک کاراکتر خاص را بخوانید.
اندیس از 0 شروع میشود.
طول رشته: خاصیت Length تعداد کاراکترهای رشته را برمیگرداند.
مثال:
string text = "Reza";
Console.WriteLine(text[0]);
Console.WriteLine(text[3]);
int length = text.Length;
Console.WriteLine($"LS: {length}");
string empty = "";
Console.WriteLine($"LES: {empty.Length}");
نکات کلیدی:
اگر اندیسی خارج از محدوده باشد (مثلاً text[10] برای رشته 4 کاراکتری)، خطای IndexOutOfRangeException رخ میدهد.
Length برای بررسی خالی بودن رشته مفید است.
دلیل اینکه میتوانیم در C# از رشته (string) با براکت و اندیس (مثل text[0] ) برای دسترسی به حروف استفاده کنیم، به علت این است که رشته ها در C# بهصورت آرایهای از کاراکترها پیادهسازی می شوند . در C#، یک string در اصل یک مجموعه از کاراکترها (char) است که بهصورت پشتسرهم در حافظه ذخیره میشوند.
رشتههای یونیکد (مثل اموجیها) ممکن است نیاز به توجه خاصی داشته باشند (در پستهای بعدی بررسی میشود).
پست 1: رشتهها در C# چیستند؟
هدف: معرفی مفهوم رشتهها و اهمیت آنها در برنامهنویسی.
محتوا: رشتهها (Strings) یکی از پرکاربردترین انواع داده در برنامهنویسی هستند. در C#، رشته یک دنباله از کاراکترهاست که برای ذخیره متن، مثل نام ، پیام ، یا دادههای متنی استفاده میشود. رشتهها در C# از نوع string هستند و به صورت immutable (غیرقابل تغییر) طراحی شدهاند، یعنی بعد از ایجاد، نمیتوان محتوای آنها را مستقیماً تغییر داد.
مثال:
string greeting = "Hello, World!";
Console.WriteLine(greeting); // خروجی: Hello, World!
string empty = "";
Console.WriteLine("رشته خالی: " + empty); // خروجی: رشته خالی:
نکات کلیدی:
آموزش کار با StreamWriter در سیشارپ : نوشتن دادهها در فایلها
در سیشارپ ، یکی از نیازهای رایج توسعهدهندگان ، نوشتن دادهها در فایلها بهصورت متنی است . کلاس StreamWriter در فضای نام System.IO ابزاری قدرتمند و ساده برای این منظور فراهم میکند. در این مقاله، بهطور جامع به بررسی StreamWriter ، نحوه استفاده از آن، ویژگیها و نکات مهم مرتبط با آن میپردازیم.
StreamWriter چیست؟
StreamWriter یک کلاس در داتنت است که برای نوشتن کاراکترها و متنها در یک جریان (Stream) بهصورت ترتیبی طراحی شده است. این کلاس معمولاً برای نوشتن دادهها در فایلهای متنی استفاده میشود و از کدگذاریهای مختلف (مانند UTF-8، ASCII و غیره) پشتیبانی میکند.
چرا از StreamWriter استفاده کنیم؟
سادگی: رابط کاربری سادهای برای نوشتن متن در فایلها یا جریانها ارائه میدهد.
انعطافپذیری: امکان استفاده از کدگذاریهای مختلف برای پشتیبانی از زبانها و کاراکترهای خاص.
مدیریت منابع: با استفاده از بلوک using ، مدیریت خودکار منابع را فراهم میکند و از نشت حافظه جلوگیری میکند.
نحوه استفاده از StreamWriter
برای شروع کار با StreamWriter باید فضای نام System.IO را به پروژه خود اضافه کنید. در ادامه، مراحل اصلی استفاده از این کلاس را بررسی میکنیم.
1 - ایجاد یک نمونه از StreamWriter
شما میتوانید یک نمونه از StreamWriter را با مشخص کردن مسیر فایل یا یک جریان (Stream) ایجاد کنید. مثال زیر نشان میدهد چگونه یک فایل متنی جدید ایجاد کرده و در آن بنویسید:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
using (StreamWriter writer = new StreamWriter(filePath))
{
writer.WriteLine("سلام رضا!");
}
Console.WriteLine("نوشتن در فایل با موفقیت انجام شد.");
}
}
در این کد:
using تضمین میکند که پس از اتمام کار ، منابع بهدرستی آزاد شوند.
WriteLine یک خط متن را در فایل مینویسد و به خط بعدی میرود.
2 - متدهای اصلی StreamWriter
StreamWriter چندین متد مفید برای نوشتن دادهها ارائه میدهد:
Write(string) : یک رشته را بدون افزودن خط جدید مینویسد.
WriteLine(string) : یک رشته را مینویسد و به خط جدید میرود.
Flush() : بافر داخلی را خالی کرده و دادهها را به جریان یا فایل منتقل میکند.
Close() : جریان را میبندد (در صورت استفاده از using نیازی به فراخوانی صریح آن نیست) .
مثال:
using (StreamWriter writer = new StreamWriter("example.txt"))
{
writer.Write("این یک متن است ");
writer.WriteLine("که در یک خط نوشته میشود.");
writer.WriteLine("خط دوم!");
}
3 - استفاده از کدگذاری (Encoding)
بهطور پیشفرض، StreamWriter از کدگذاری UTF-8 استفاده میکند، اما میتوانید کدگذاری دیگری را مشخص کنید. برای مثال:
using (StreamWriter writer = new StreamWriter("example.txt", false, System.Text.Encoding.UTF32))
{
writer.WriteLine("متن با کدگذاری UTF-32");
}
پارامتر دوم (false) مشخص میکند که فایل از نو نوشته شود (در صورت true، به انتهای فایل اضافه میشود).
نکات مهم در استفاده از StreamWriter
مدیریت استثناها: هنگام کار با فایلها، ممکن است خطاهایی مانند عدم دسترسی به فایل رخ دهد. بهتر است از بلوک try-catch استفاده کنید:
try
{
using (StreamWriter writer = new StreamWriter("example.txt"))
{
writer.WriteLine("متن آزمایشی");
}
}
catch (IOException ex)
{
Console.WriteLine("خطا در نوشتن فایل: " + ex.Message);
}
اضافه کردن به فایل (Append) : برای افزودن متن به انتهای فایل موجود، از پارامتر append استفاده کنید:
using (StreamWriter writer = new StreamWriter("example.txt", true))
{
writer.WriteLine("متن جدید به انتها اضافه شد.");
}
بستن جریان: اگر از using استفاده نکنید، باید بهصورت دستی Close() یا Dispose() را فراخوانی کنید تا منابع آزاد شوند.
مثال کاربردی: ثبت لاگها
یکی از کاربردهای رایج StreamWriter ، ثبت لاگها در یک فایل است:
public class Logger
{
private readonly string logFilePath = "log.txt";
public void Log(string message)
{
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine($"{DateTime.Now}: {message}");
}
}
}
class Program
{
static void Main()
{
Logger logger = new Logger();
logger.Log("برنامه شروع شد.");
logger.Log("یک عملیات انجام شد.");
}
}
تفاوت StreamWriter با File.WriteAllText
شاید بپرسید چرا از StreamWriter استفاده کنیم وقتی متدهای سادهتری مثل File.WriteAllText وجود دارند؟ پاسخ این است:
File.WriteAllText برای نوشتن یکباره کل متن مناسب است، اما کنترل کمی ارائه میدهد.
StreamWriter برای نوشتن ترتیبی و در سناریوهایی که نیاز به مدیریت جریان یا نوشتن خط به خط دارید، ایدهآل است.
جمعبندی
StreamWriter ابزاری قدرتمند و انعطافپذیر برای نوشتن دادههای متنی در فایلها و جریانها در سیشارپ است. با استفاده از این کلاس، میتوانید بهراحتی فایلهای متنی ایجاد کنید، به آنها اضافه کنید و حتی کدگذاری دلخواه خود را اعمال کنید. با رعایت نکاتی مثل مدیریت استثناها و آزادسازی منابع، میتوانید کدی تمیز و کارآمد بنویسید.
ششمین شکل از انواع ارثبری: ارثبری رابطها (Interface Inheritance) در C#
در سلسله آموزشهای ارثبری در برنامهنویسی شیءگرا با C#، تاکنون با انواع مختلفی از ارثبری مانند تکپایه، چندسطحی، سلسلهمراتبی و ترکیبی آشنا شدهایم. حالا در ششمین و آخرین بخش از این مجموعه، به سراغ یکی از مهمترین و کاربردیترین مفاهیم در C# میرویم: ارثبری رابطها (Interface Inheritance) این نوع ارثبری به ما اجازه میدهد از قابلیتهای ارثبری چندگانه به شکلی امن و کنترلشده استفاده کنیم. بیایید با جزئیات ، این موضوع را بررسی کنیم.
ارثبری رابطها چیست؟
در زبان C#، برخلاف برخی زبانها مثل C++ که از ارثبری چندگانه برای کلاسها پشتیبانی میکنند، ارثبری چندگانه فقط از طریق رابطها (Interfaces) امکانپذیر است. رابطها قراردادهایی هستند که مشخص میکنند یک کلاس چه رفتارهایی باید داشته باشد، بدون اینکه پیادهسازی آن رفتارها را اجبار کنند. این ویژگی به برنامهنویسان اجازه میدهد انعطافپذیری بیشتری در طراحی سیستمهای خود داشته باشند.
مثال مفهومی در C#
برای درک بهتر، فرض کنید میخواهیم کلاسی برای یک اردک طراحی کنیم که هم بتواند راه برود و هم شنا کند. اینجاست که ارثبری رابطها به کار میآید:
public interface IWalkable
{
void Walk();
}
public interface ISwimmable
{
void Swim();
}
public class Duck : IWalkable, ISwimmable
{
public void Walk() => Console.WriteLine("Walking...");
public void Swim() => Console.WriteLine("Swimming...");
}
class Program
{
static void Main()
{
Duck duck = new Duck();
duck.Walk(); // خروجی: Walking...
duck.Swim(); // خروجی: Swimming...
}
}
در این مثال:
رابط IWalkable متد Walk را تعریف میکند.
رابط ISwimmable متد Swim را تعریف میکند.
کلاس Duck هر دو رابط را پیادهسازی میکند و به این ترتیب از ارثبری چندگانه رابطها استفاده میکند.
نکات کلیدی در ارثبری رابطها
برای استفاده صحیح از این نوع ارثبری، باید به چند نکته مهم توجه کنیم:
ارثبری یگانه برای کلاسها: در C#، یک کلاس فقط میتواند از یک کلاس دیگر ارث ببرد (Single Inheritance). این محدودیت برای جلوگیری از پیچیدگیهای ناخواسته طراحی شده است.
ارثبری چندگانه با رابطها: برخلاف کلاسها، یک کلاس میتواند چندین رابط را پیادهسازی کند. این ویژگی به ما اجازه میدهد رفتارهای متنوعی را به یک کلاس اضافه کنیم.
مسئله الماس (Diamond Problem): در ارثبری چندگانه کلاسها (که در C# پشتیبانی نمیشود)، اگر دو کلاس والد یک متد یکسان را از یک پایه مشترک ارث ببرند، ابهام ایجاد میشود (به این مشکل، مسئله الماس میگویند). رابطها این مشکل را ندارند، زیرا فقط تعریف متد را ارائه میدهند و پیادهسازی بر عهده کلاس است.
اصل جایگزینی لیسکوف (Liskov Substitution Principle): در طراحی ارثبری، باید اطمینان حاصل کنیم که هر کلاس فرزند بتواند بدون تغییر رفتار مورد انتظار، جایگزین کلاس والد شود. این اصل به خصوص در استفاده از رابطها اهمیت دارد تا انسجام سیستم حفظ شود.
مزایا و کاربردها
ارثبری رابطها به ما امکان میدهد:
کد را ماژولار و قابلتست کنیم.
وابستگیها را کاهش دهیم (مثلاً با استفاده از تزریق وابستگی).
از الگوهای طراحی مثل Strategy یا Factory به شکلی تمیز استفاده کنیم.
نتیجهگیری
ارثبری رابطها به عنوان ششمین و آخرین شکل از انواع ارثبری در این سلسله آموزشها، یکی از قدرتمندترین ابزارهای C# برای طراحی سیستمهای منعطف و قابلگسترش است. در حالی که محدودیت ارثبری یگانه برای کلاسها ما را به نظم و سادگی تشویق میکند، رابطها دریچهای به سوی انعطافپذیری و چندمنظوره بودن باز میکنند. دفعه بعد که در حال طراحی یک پروژه هستید، به این فکر کنید که چگونه میتوانید از رابطها برای بهبود ساختار کد خود استفاده کنید. با این دانش، حالا آمادهاید تا ارثبری را در C# به شکلی حرفهای به کار ببرید!
توضیح راجع به سینتکس کد
این شیوه نوشتن کد که از عملگر => استفاده میکند، به نام Expression-Bodied Method شناخته میشود و در C# 6.0 معرفی شده است. این روش به شما اجازه میدهد متدهای تکخطی را به شکلی مختصر و خوانا بنویسید. به جای استفاده از بلوک {} و دستور return (در صورت نیاز)، میتوانید از => برای تعریف مستقیم بدنه متد استفاده کنید. برای مثال،
public void Walk() => Console.WriteLine("Walking...");
معادل
public void Walk()
{
Console.WriteLine("Walking...");
}
است، اما کوتاهتر و تمیزتر. این سبک معمولاً برای متدهای ساده و بدون منطق پیچیده استفاده میشود. برای بخاطر سپاری راحت آن می توانید آن را شبیه به حالتی در نظر بگیرید که یک جمله شرطی فقط یک عمل در پی دارد( بدون هیچ منطق و یا کد اضافی) در چنین حالتی همانطور که شاید می دانستید از آکولاد استفاده نمی کنیم.
if (x > 0) Console.WriteLine("عدد مثبت است");
البته اشتباه نشود عملگر => برای متدها بکار می رود و مثالی که زدم فقط برای بخاطر سپردن مطلب بود