โหมดมืด
บทที่ 1 — Setup + Hello World แบบเข้าใจทุกบรรทัด
ลงมือเขียน Hello World ก่อน — แล้วเราจะค่อย ๆ ถอด syntax ทีละชิ้น
1. ติดตั้ง .NET SDK
ก่อนเขียน C# ต้องติดตั้ง ".NET SDK" ก่อน
- SDK = Software Development Kit (ชุดเครื่องมือสำหรับนักพัฒนา)
- หน้าที่หลัก: compile (แปลโค้ดให้เครื่องเข้าใจ) + รัน + จัดการ project
หนังสือนี้เขียนโดยเล็งเป้าไปที่ .NET 10 (LTS รุ่นใหม่) แต่ทุกตัวอย่างก็รันได้เหมือนกันบน .NET 8 LTS (baseline ที่บทที่ 0 ใช้) — เลือกติดตั้งเวอร์ชันไหนก็ได้ระหว่างสองอันนี้ตามที่มีให้ติดตั้งจริงตอนที่คุณอ่าน
⚠️ ตรวจสอบก่อนติดตั้งเสมอ: .NET ออกเวอร์ชันใหม่ทุกปี ถ้า
Microsoft.DotNet.SDK.10(คำสั่งด้านล่าง) หา package ไม่เจอหรือยังไม่พร้อมใช้งานตอนที่คุณอ่าน ให้เช็คเวอร์ชัน LTS ล่าสุดที่หน้า https://dotnet.microsoft.com/download ก่อน แล้วสลับไปติดตั้งMicrosoft.DotNet.SDK.8แทนได้เลย (ตัวอย่างในเล่มนี้ใช้ได้ทั้งคู่)
วิธีติดตั้งต่างกันตามระบบปฏิบัติการ เสร็จแล้วตรวจด้วย dotnet --version:
Windows:
powershell
winget install Microsoft.DotNet.SDK.10
# ถ้าไม่เจอ package (ยังไม่ GA ตอนที่คุณอ่าน) ลองรุ่น 8 แทน:
# winget install Microsoft.DotNet.SDK.8
# หรือดาวน์โหลดจาก https://dotnet.microsoft.com/downloadmacOS:
bash
brew install --cask dotnet-sdk
# หมายเหตุ: คำสั่งนี้ติดตั้ง SDK รุ่นล่าสุดที่ Homebrew มีให้ — อาจไม่ใช่ .NET 10
# เพื่อระบุเวอร์ชัน ดาวน์โหลดจาก https://dotnet.microsoft.com/download แทน
# หลังติดตั้งแล้วรัน dotnet --version เพื่อตรวจว่าได้เวอร์ชันที่ต้องการLinux (Ubuntu):
bash
# ต้องเพิ่ม Microsoft package feed ก่อน (ถ้ายังไม่มี) — ไม่งั้น apt หา package ไม่เจอ
# วิธีละเอียด: https://learn.microsoft.com/dotnet/core/install/linux-ubuntu
sudo apt install -y dotnet-sdk-10.0ตรวจสอบ:
bash
dotnet --version # 10.0.x (หรือ 8.0.x / 9.0.x ก็ใช้ได้)
dotnet --list-sdks
dotnet --list-runtimes2. IDE — เลือกอะไรดี
| IDE | จุดเด่น | เหมาะกับ |
|---|---|---|
| Visual Studio 2022 | feature เยอะที่สุด (Windows only) | enterprise, GUI dev |
| VS Code + C# Dev Kit | เบา, cross-platform (ใช้ได้ทุกระบบ Win/Mac/Linux) | งานทั่วไป |
| JetBrains Rider | ดีมาก (paid) | คนที่ใช้ IntelliJ อยู่แล้ว |
หนังสือเล่มนี้ใช้ VS Code + C# Dev Kit extension
ติดตั้ง C# Dev Kit (ครั้งเดียวจบ):
- เปิด VS Code → คลิกไอคอน Extensions (แถบซ้าย) หรือกด
Ctrl+Shift+X - พิมพ์ค้นหา
C# Dev Kit(ของ Microsoft) - กด Install — extension จะลง
C#(base) ให้อัตโนมัติด้วย
3. สร้างโปรเจกต์แรก
.NET ใช้คำสั่ง dotnet จัดการทุกอย่าง — dotnet new สร้างโปรเจกต์จาก template (แม่แบบสำเร็จรูป) ส่วน dotnet run คอมไพล์แล้วรันให้เลย เริ่มจากโปรเจกต์ชนิดง่ายที่สุดก่อน คือ console app
console app คือโปรแกรมที่ทำงานบนหน้าจอ terminal (หน้าต่างพิมพ์คำสั่ง — ไม่มี UI แบบหน้าต่าง/ปุ่มกด) เช่น Command Prompt หรือ PowerShell บน Windows, Terminal บน macOS
💡 รันคำสั่งใน terminal ที่ระบบมี — PowerShell หรือ Windows Terminal บน Windows, Terminal บน macOS/Linux (ไม่ใช่ bash ของ Linux โดยเฉพาะ — Windows ก็ใช้ได้)
bash
# template ชนิด console
dotnet new console -n HelloCSharp
cd HelloCSharp
# รัน
dotnet runผลลัพธ์:
text
Hello, World!ไฟล์ที่ template สร้าง:
text
HelloCSharp/
├── HelloCSharp.csproj ← project file (ไฟล์ตั้งค่าโปรเจกต์ = build config + dependencies คือ library ที่โปรเจกต์พึ่งพา)
├── Program.cs ← source code (โค้ดต้นฉบับ)
└── obj/, bin/ ← build output (ผลลัพธ์หลัง build)4. แตก Program.cs ทีละบรรทัด
csharp
// Program.cs (แบบใหม่ .NET 6+)
Console.WriteLine("Hello, World!");เห็นไหม — แค่ 1 บรรทัด — นี่คือ top-level statement:
- statement = คำสั่งที่ทำอะไรสักอย่าง 1 อย่าง (เช่นพิมพ์ออกจอ) จบด้วย
; - top-level = เขียนลอย ๆ ระดับบนสุดของไฟล์ได้เลย ไม่ต้องห่อด้วย class/method ก่อน (class และ method คือแนวคิดที่จะเรียนในบทที่ 4-5) ตอนนี้รู้แค่ว่า C# สมัยใหม่ไม่บังคับให้ห่อโค้ดด้วยโครงสร้างพิเศษก็พอ
- ฟีเจอร์นี้มาตั้งแต่ C# 9
💡
Consoleมาจากไหน? —Consoleเป็น class ที่ .NET มีให้อยู่แล้วใน namespace ชื่อSystem(เช่นSystem.Console) และเนื่องจาก csproj มี<ImplicitUsings>enable</ImplicitUsings>(หัวข้อ 5) จึงไม่ต้องเขียนusing System;เองก่อนใช้Console— เห็นรายละเอียดใน หัวข้อ 6
"อ้าว ไม่ต้องมี class? ไม่ต้องมี Main?"
ก่อน C# 9 ต้องเขียนแบบนี้ — นิยามสั้น ๆ เพื่อไม่ให้งง (อย่ากังวลถ้ายังไม่เข้าใจ 100% — บท 2/4/5 อธิบายเต็ม):
namespace= ชื่อกลุ่มโค้ด ป้องกันชื่อซ้ำกัน (อ้างอิงในหัวข้อ 6 และบทที่ 10)class= แม่แบบสำหรับสร้าง object (เรียนเต็มบทที่ 5)static= เรียกได้เลยโดยไม่ต้องสร้าง object ก่อน (เรียนเต็มบทที่ 4)
csharp
namespace HelloCSharp
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Hello, World!");
}
}
}หลัง C# 9 compiler "เติม" namespace + class + static void Main(string[] args) ให้อัตโนมัติ — เขียน statement ตรง ๆ ใน Program.cs ก็พอ
implicit using (การ import อัตโนมัติ)
ตั้งแต่ .NET 6 เป็นต้นมา ไม่ต้องเขียน using System; (คำสั่งดึง namespace มาใช้) เองอีกต่อไป เพราะ csproj มี <ImplicitUsings>enable</ImplicitUsings> ซึ่งดึง namespace ที่ใช้บ่อย ๆ มาให้อัตโนมัติ
(namespace คือกลุ่มโค้ดที่จัดหมวดหมู่ร่วมกัน เช่น Console อยู่ในหมวด System — หัวข้อ 6 อธิบายละเอียด)
5. ดู csproj
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType> <!-- สร้างไฟล์ .exe ที่รันได้ -->
<TargetFramework>net10.0</TargetFramework><!-- เล็งใช้ .NET 10 (LTS) -->
<ImplicitUsings>enable</ImplicitUsings> <!-- มี using System; ให้อัตโนมัติ -->
<Nullable>enable</Nullable> <!-- เปิด nullable reference types -->
</PropertyGroup>
</Project>Field (ช่องตั้งค่า) สำคัญ:
TargetFramework=net10.0,net9.0, ... — เลือกเวอร์ชัน .NET ที่ target (เล็งจะใช้)Nullable=enable= ต้องเปิด! — เปิด Nullable Reference Types (ระบบที่ compiler ช่วยเตือนเวลาใช้ค่าที่อาจเป็น null ผิด ๆ) จะเตือนถ้าใช้ null ผิด (เรื่องใหญ่ — เดี๋ยวบท 2 อธิบายเต็ม)ImplicitUsings=enable= ไม่ต้องusing System;ทุกไฟล์
6. รูปแบบเต็ม (กลับมาดูตอนต้องเข้าใจลึก)
csharp
// using = ดึง namespace มาใช้ (เหมือน import ใน Java/TS)
// ตั้งแต่มี implicit usings — System, System.Collections.Generic, System.IO, System.Linq ฯลฯ มาให้แล้ว
using System;
// namespace = การจัดกลุ่มโค้ด (เหมือน package ใน Java)
// file-scoped namespace (namespace แบบทั้งไฟล์, C# 10+) — ไม่มี { } ครอบทั้งไฟล์
namespace HelloCSharp;
// class = แม่แบบของ object (วัตถุ)
class Program
{
// Main = จุดเริ่มโปรแกรม (entry point) — runtime มองหา method ที่มีรูปแบบ (signature) นี้
// static = เรียกได้เลยโดยไม่ต้องสร้าง object ก่อน
// void = ไม่คืนค่ากลับ
// string[] args = อาร์กิวเมนต์ (ค่าที่ส่งเข้ามา) จาก command line — เป็น array ของ string
static void Main(string[] args)
{
// Console = static class อยู่ใน namespace ชื่อ System
// WriteLine = พิมพ์ออกจอ + ขึ้นบรรทัดใหม่ (new line)
Console.WriteLine("Hello, World!");
}
}ทุกคำมีเหตุผล — อย่ากังวลถ้ายังไม่เข้าใจ static / string[] ตอนนี้ เดี๋ยวบท 2/4 จะอธิบาย
7. รับ input + arguments
โปรแกรมรับข้อมูลจากผู้ใช้ได้ 2 ทาง — command line argument (args ตอนเรียกโปรแกรม) และอ่านจากแป้นพิมพ์ระหว่างทำงาน (Console.ReadLine()):
command line argument
csharp
// ใน top-level statement ใช้ตัวแปรพิเศษชื่อ args ได้เลย (มาให้เอง)
// args.Length = จำนวน argument ที่ส่งเข้ามา
// args[0] = argument ตัวแรก (ดัชนีเริ่มที่ 0 — บท 2 อธิบาย array)
// $"..." = string interpolation (แทรกค่าตัวแปรในข้อความ — หัวข้อ 8 อธิบายเต็ม)
if (args.Length > 0)
Console.WriteLine($"Hello, {args[0]}!");
else
Console.WriteLine("Hello, stranger!");bash
dotnet run -- Alice
# > Hello, Alice!
# (-- เพื่อบอก dotnet ว่า อาร์กิวเมนต์หลังนี้เป็นของโปรแกรมเรา ไม่ใช่ของคำสั่ง dotnet)Read line (อ่านบรรทัดที่ผู้ใช้พิมพ์)
csharp
Console.Write("Name? ");
// string? = string ที่อาจเป็น null ได้ — เครื่องหมาย ? ท้าย type บอกว่า "null ได้นะ"
// (บท 2 อธิบาย NRT เต็ม ตอนนี้รู้แค่ว่า Console.ReadLine() อาจคืน null)
string? name = Console.ReadLine(); // อาจคืน null ถ้า stdin (ช่องรับข้อมูลเข้า) ถูกปิด
// ?? = null-coalescing = "ถ้าฝั่งซ้ายเป็น null ให้ใช้ฝั่งขวาแทน"
Console.WriteLine($"Hello, {name ?? "stranger"}!");?? = null-coalescing (ตัวเลือกค่าสำรองเมื่อเป็น null) — ถ้าค่าฝั่งซ้าย = null ให้ใช้ค่าฝั่งขวาแทน บท 2 อธิบาย string? และ null handling เต็ม ๆ
8. String interpolation
C# แทรกค่าตัวแปรลงในข้อความด้วย string interpolation (การแทรกค่าในข้อความ) — เติม $ หน้า string แล้วใส่ตัวแปรใน { } (อ่านง่ายกว่าการต่อ string ด้วย +):
csharp
string name = "Alice";
int age = 30;
// แบบเก่า
Console.WriteLine("Hello, " + name + "! Age = " + age);
// แบบดี (C# 6+) — string interpolation ด้วย $
Console.WriteLine($"Hello, {name}! Age = {age}");
// จัดรูปแบบตัวเลข
double price = 1234.5678;
Console.WriteLine($"Price: {price:N2}"); // 1,234.57
Console.WriteLine($"Time: {DateTime.Now:yyyy-MM-dd HH:mm}");
// verbatim string (string ดิบ ขึ้นต้นด้วย @ — ไม่ต้อง escape \) + interpolation
// ปกติ \n = ขึ้นบรรทัดใหม่, \\ = backslash 1 ตัว; @ ปิดการตีความ \ ทำให้พิมพ์เหมือนที่เห็น
string path = $@"C:\Users\{name}\file.txt";⏭️ โซนขั้นสูง — มือใหม่ข้ามได้
บล็อกข้างล่างเป็น raw string literal (string หลายบรรทัดแบบดิบ ไม่ต้อง escape) ผสมกับการแทรกตัวแปร ซึ่งหน้าตาแปลกตา (ต้องใช้
$$และ{{ }}) บทแรกใช้แค่$"..."ก็พอแล้ว ข้ามตรงนี้ไปก่อนได้ เดี๋ยวบท 10 อธิบาย raw string เต็ม ๆcsharp// raw string literal (C# 11+) — ไม่ต้อง escape เลย // ใช้ $$ และ {{ }} เพราะข้อความมี { } อยู่แล้ว (ของ JSON) string json = $$""" { "name": "{{name}}", "age": {{age}} } """;
9. Build vs Run vs Publish
bash
# compile + รัน (workflow ตอน develop)
dotnet run
# build เฉย ๆ — ไม่รัน
dotnet build
# build แบบ Release (optimized คือปรับให้เร็วที่สุด)
dotnet build -c Release⏭️ เนื้อหาส่วนที่เหลือของหัวข้อนี้ (publish + AOT + self-contained) เป็นเรื่อง deploy ซึ่งยังไม่จำเป็นตอนนี้ — มือใหม่ข้ามไป §10 ได้เลย แล้วกลับมาอ่านตอนเตรียมขึ้น production จริง (หรือดูบทท้ายเล่ม)
bash
# ทำเป็นไฟล์รัน (binary) ที่เอาไป deploy (นำขึ้นใช้งานจริง) ได้
dotnet publish -c Release -r linux-x64 --self-contained
# → bin/Release/net10.0/linux-x64/publish/HelloCSharp (ไฟล์รันไฟล์เดียว)
# บน Windows: เปลี่ยน -r เป็น win-x64 → ได้ HelloCSharp.exe
# AOT (Ahead-Of-Time = compile เป็นภาษาเครื่องตั้งแต่ build, .NET 7+)
dotnet publish -c Release -r linux-x64 -p:PublishAot=true
# → ไฟล์รันเล็กลง, startup เร็ว, ไม่ต้องมี runtime ที่ serverSelf-contained vs Framework-dependent (รวม runtime มาเอง vs พึ่ง runtime ของ server):
- runtime = โปรแกรมพื้นฐานที่ต้องมีก่อนถึงจะรัน .NET app ได้ (เหมือน Java ต้องมี JRE)
- Self-contained = รวม .NET runtime ไว้ในไฟล์รัน → ใหญ่กว่า แต่ server ไม่ต้องติดตั้ง .NET
- Framework-dependent (ค่า default) = server ต้องมี .NET runtime เวอร์ชันตรงกัน
10. โครงสร้างหลายไฟล์
ลองเพิ่มไฟล์:
csharp
// Greeter.cs
namespace HelloCSharp;
public class Greeter
{
// => คือ expression-bodied method — ย่อของ { return $"Hello, {name}!"; } (บท 4 อธิบาย)
public string Hello(string name) => $"Hello, {name}!";
}ใช้ใน Program.cs:
csharp
using HelloCSharp;
// using HelloCSharp; = ดึง namespace HelloCSharp มาใช้ — เพราะ Greeter.cs ใช้ namespace HelloCSharp
// ถ้าทั้งสองไฟล์ใช้ namespace เดียวกันอยู่แล้ว ก็ไม่ต้องใส่ using เลย
// var = ให้ compiler เดา type จากฝั่งขวา → ที่นี่ var = Greeter (บท 2 อธิบาย var เต็ม)
var greeter = new Greeter();
Console.WriteLine(greeter.Hello("Alice"));💡 namespace คืออะไร? — namespace คือชื่อ "กลุ่ม" ของโค้ด ถ้า class อยู่ใน namespace อื่น ต้อง
usingบอกก่อน ถ้า namespace เดียวกัน (เช่นทั้งสองไฟล์ใช้namespace HelloCSharp;) ก็ใช้ได้เลยโดยไม่ต้องusing⚠️ Pitfall (จุดพลาดบ่อย): ถ้า namespace ไม่ตรงกัน — ต้อง
usingหรือใช้ชื่อเต็ม (new HelloCSharp.Greeter())
11. ทำให้ "run" เร็วใน dev
ระหว่างพัฒนา ไม่อยากสั่ง dotnet run ใหม่ทุกครั้งที่แก้โค้ด — ใช้ dotnet watch ที่คอย reload อัตโนมัติเมื่อไฟล์เปลี่ยน ทำให้รอบการพัฒนา (dev loop) เร็วขึ้น:
bash
# watch mode — โหลดใหม่อัตโนมัติเมื่อ file เปลี่ยน
dotnet watch runใน VS Code: กด F5 (รันแบบ debug = ตามรอยหาบั๊กได้) หรือ Ctrl+F5 (รันเฉย ๆ ไม่ debug)
12. NuGet — เพิ่ม package
ข่าวดี: งาน JSON ทั่วไปไม่ต้องลง package — System.Text.Json ติดมากับ .NET เลย (built-in, เร็ว, แนะนำสำหรับโค้ดใหม่):
csharp
using System.Text.Json;
// new { ... } = anonymous object (object ไร้ชื่อ) — บทหลังอธิบาย
// ตอนนี้รู้แค่ว่ามันคือ "สร้าง object 1 ตัวที่มี property name+age"
// หมายเหตุ: สำหรับ AOT publish (หัวข้อ 9) แนะนำใช้ named type + [JsonSerializable] แทน
var data = new { name = "Alice", age = 30 };
string json = JsonSerializer.Serialize(data);
Console.WriteLine(json); // {"name":"Alice","age":30}เพิ่ม package ภายนอกตอนต้องการจริง ๆ — ตัวอย่างเช่น HTTP client เพิ่มเติม, ORM, logging:
bash
dotnet add package Serilog
# → เพิ่มบรรทัด <PackageReference> (การอ้างอิง package) ใน csproj⚠️ ทำไมไม่ใช้ Newtonsoft.Json? เป็น library JSON เก่า (legacy) ที่ครองตลาดยุค .NET Framework — ยังพบในโปรเจกต์เก่ามาก แต่โค้ดใหม่ใช้
System.Text.Json(เร็วกว่า, alloc น้อยกว่า, ไม่ต้องลง package) บทที่ 12 จะลงรายละเอียดทั้งคู่
13. Common Pitfalls (จุดพลาดบ่อย) มือใหม่
- รันไฟล์
.csตรง ๆ ไม่ได้ — ต้องอยู่ใน project (มี.csproj) Nullable=enableแต่ใช้ null — compiler เตือน (warning) เพียบ → ต้องเรียนรู้ NRT (บทถัดไป)Console.WriteLinevsConsole.Write— Write ไม่ขึ้นบรรทัดใหม่ (ไม่มี newline) ท้าย- ไม่ต้องกังวลเรื่อง local function ใน top-level statement — เขียนได้ปกติโดยไม่ต้องใส่
staticเพิ่ม (นี่ไม่ใช่จุดพลาด แค่กันงงล่วงหน้าเผื่อสงสัย) - case sensitive (ตัวพิมพ์เล็ก/ใหญ่มีผล) —
console≠Console - ลืม semicolon
;(เครื่องหมายจบคำสั่ง) — error
🛠️ Checkpoint 1 — ลงมือทำ
- สร้างโปรเจกต์
Greetings - รับชื่อจาก command-line:
dotnet run -- Alice 30→ "Hello, Alice (30)!" - ถ้าไม่มี arg → ถามผ่าน
Console.ReadLine() - แยกเป็น 2 file:
Program.cs+Greeter.cs(มี methodHello(string, int)) - ลอง
dotnet publish -c Release -r <your-os>-x64 --self-containedแล้วลอง run binary โดยตรง (ไม่ผ่าน dotnet)
สรุปบทที่ 1
- ติดตั้ง .NET SDK + เปิด
dotnet new console - C# 9+ มี top-level statement →
Program.csแค่ statement ก็พอ .csproj= build config — สำคัญที่Nullable=enable,ImplicitUsings=enable- string interpolation
$"...", raw string"""...""" dotnet run/build/publish= workflow หลัก- NuGet =
dotnet add package