ความไม่เปลี่ยนแปลงด้วย init และ with
ใช้ตัวกำหนดค่าแบบ init เท่านั้นเพื่อสร้างออบเจ็กต์ที่ไม่เปลี่ยนแปลง และใช้นิพจน์ with เพื่อสร้างสำเนาที่แก้ไขโดยไม่ทำลายออบเจ็กต์เดิม
ความไม่เปลี่ยนแปลงด้วย init และ with เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
ความไม่เปลี่ยนแปลงคืออะไร
ออบเจ็กต์ที่ ไม่เปลี่ยนแปลง จะไม่สามารถเปลี่ยนแปลงได้หลังจากสร้างแล้ว ความไม่เปลี่ยนแปลงช่วยป้องกันการเปลี่ยนค่าโดยไม่ตั้งใจ ทำให้ออบเจ็กต์ปลอดภัยต่อเธรดโดยค่าเริ่มต้น และทำให้การทำความเข้าใจโค้ดง่ายขึ้น C# มีตัวตั้งค่า init และนิพจน์ with เพื่อให้การสร้างออบเจ็กต์ที่ไม่เปลี่ยนแปลงทำได้สะดวก
ตัวตั้งค่าที่ใช้ได้เฉพาะกับ init
ตัวเข้าถึง init อนุญาตให้กำหนดคุณสมบัติระหว่างการเริ่มต้นออบเจ็กต์ เช่น ในตัวสร้างหรือตัวเริ่มต้นออบเจ็กต์ แต่จะไม่สามารถกำหนดได้อีกหลังจากนั้น นี่คือรูปแบบที่ไม่เปลี่ยนแปลงของ set
public class Point
{
public double X { get; init; }
public double Y { get; init; }
}
var p = new Point { X = 3.0, Y = 4.0 }; // OK — init phase
p.X = 5.0; // COMPILE ERROR — cannot set after init
// All of these are valid init-phase assignments:
var p2 = new Point(X: 1.0, Y: 2.0);
var p3 = new Point { X = 0, Y = 0 };init ในคุณสมบัติแบบระบุตำแหน่งของเรคคอร์ด
คุณสมบัติแบบระบุตำแหน่งของเรคคอร์ดจะใช้ init เท่านั้นโดยค่าเริ่มต้น ซึ่งเป็นสิ่งที่ทำให้เรคคอร์ดไม่เปลี่ยนแปลง คอมไพเลอร์จะสร้าง get; init; ให้กับพารามิเตอร์แบบระบุตำแหน่งแต่ละรายการ
// This record:
public record Person(string Name, int Age);
// Is equivalent to:
public record Person
{
public string Name { get; init; }
public int Age { get; init; }
public Person(string Name, int Age) { this.Name = Name; this.Age = Age; }
// + Deconstruct, Equals, GetHashCode, ToString
}
var alice = new Person("Alice", 30);
alice.Name = "Bob"; // COMPILE ERRORนิพจน์ with: การเปลี่ยนแปลงแบบไม่ทำลายข้อมูล
นิพจน์ with จะสร้างสำเนาของเรคคอร์ดพร้อมเปลี่ยนคุณสมบัติบางรายการ เรคคอร์ดเดิมจะไม่เปลี่ยนแปลง จึงยังคงไม่เปลี่ยนแปลงอย่างแท้จริง
public record Person(string Name, int Age, string Email);
var alice = new Person("Alice", 30, "alice@example.com");
// Create a modified copy — alice is unchanged
var olderAlice = alice with { Age = 31 };
var renamed = alice with { Name = "Alicia", Email = "alicia@example.com" };
Console.WriteLine(alice.Name); // Alice (unchanged)
Console.WriteLine(olderAlice.Age); // 31
Console.WriteLine(renamed.Name); // Aliciawith กับชนิดที่ไม่ใช่เรคคอร์ด
C# 10 อนุญาตให้ใช้ with กับสตรักต์หรือคลาสใด ๆ ที่มีความหมายของตัวสร้างสำเนา แต่การใช้งานกับเรคคอร์ดเป็นธรรมชาติมากที่สุด สำหรับคลาส คุณต้องนำความสามารถนี้ไปสร้างเอง
// struct with with-expression:
public struct Temperature
{
public double Celsius { get; init; }
public double Fahrenheit => Celsius * 9 / 5 + 32;
}
var t1 = new Temperature { Celsius = 20 };
var t2 = t1 with { Celsius = 25 }; // copy with change
Console.WriteLine(t1.Celsius); // 20 — unchanged
Console.WriteLine(t2.Celsius); // 25การเชื่อมโยงนิพจน์ with
เชื่อมโยงนิพจน์ with หลายรายการเพื่อสร้างการแปลงที่ซับซ้อนขึ้น โดยแต่ละขั้นตอนจะสร้างค่าที่ไม่เปลี่ยนแปลงใหม่จากค่าก่อนหน้า
public record Order(int Id, string Status, decimal Total, DateTime UpdatedAt);
var order = new Order(42, "Pending", 99.99m, DateTime.UtcNow);
// Apply a promotion discount then mark as confirmed
var finalOrder = order
with { Total = order.Total * 0.9m } // 10% off
with { Status = "Confirmed" }
with { UpdatedAt = DateTime.UtcNow };
Console.WriteLine(order.Status); // Pending (original unchanged)
Console.WriteLine(finalOrder.Status); // Confirmedคุณสมบัติที่คำนวณได้บนชนิดที่ไม่เปลี่ยนแปลง
คุณสมบัติที่ได้มาจากชนิดที่ไม่เปลี่ยนแปลงจะบริสุทธิ์ตามธรรมชาติ โดยคำนวณจากค่าคุณสมบัติที่ไม่เปลี่ยนแปลง และจะส่งคืนผลลัพธ์เดิมเสมอเมื่อได้รับข้อมูลเข้าเดียวกัน
public record Money(decimal Amount, string Currency)
{
public Money Add(Money other)
{
if (Currency != other.Currency)
throw new InvalidOperationException("Currency mismatch");
return this with { Amount = Amount + other.Amount };
}
public Money Multiply(decimal factor) =>
this with { Amount = Amount * factor };
public override string ToString() =>
$"{Amount:F2} {Currency}";
}
var price = new Money(10.00m, "USD");
var tax = price.Multiply(0.08m);
var total = price.Add(tax);
Console.WriteLine(total); // 10.80 USDคอลเลกชันที่ไม่เปลี่ยนแปลง
ผสานเรคคอร์ดเข้ากับ ImmutableList<T> และชนิดอื่น ๆ จาก System.Collections.Immutable เพื่อสร้างโครงสร้างข้อมูลที่ไม่เปลี่ยนแปลงอย่างสมบูรณ์
using System.Collections.Immutable;
public record ShoppingCart(
string UserId,
ImmutableList<CartItem> Items)
{
public ShoppingCart AddItem(CartItem item) =>
this with { Items = Items.Add(item) };
public ShoppingCart RemoveItem(int productId) =>
this with { Items = Items.RemoveAll(i => i.ProductId == productId) };
public decimal Total => Items.Sum(i => i.Price * i.Quantity);
}ความปลอดภัยต่อเธรดจากความไม่เปลี่ยนแปลง
ออบเจ็กต์ที่ไม่เปลี่ยนแปลงจะปลอดภัยต่อเธรดโดยธรรมชาติ ไม่จำเป็นต้องซิงโครไนซ์เมื่อแชร์ออบเจ็กต์ข้ามเธรด เพราะสถานะของออบเจ็กต์ไม่สามารถเปลี่ยนแปลงได้หลังการสร้าง
// Immutable config record shared across all threads safely
public record AppConfig(
string ConnectionString,
int MaxRetries,
TimeSpan Timeout);
// Register as singleton — safe because record is immutable
builder.Services.AddSingleton(
new AppConfig(
ConnectionString: config["DB"]!,
MaxRetries: 3,
Timeout: TimeSpan.FromSeconds(30)));
// Any thread can read this simultaneously without locksการใช้งานจริง: การสร้างสถานะจากเหตุการณ์เชิงฟังก์ชัน
เรคคอร์ดที่ไม่เปลี่ยนแปลงเข้ากันได้อย่างเป็นธรรมชาติกับการสร้างสถานะจากเหตุการณ์ เหตุการณ์ของโดเมนแต่ละรายการจะไม่เปลี่ยนแปลง และสถานะจะได้มาจากการพับรวมเหตุการณ์ จึงไม่มีการเปลี่ยนค่าและไม่มีเรื่องไม่คาดคิด
public record OrderState(
int Id,
string Status = "Draft",
decimal Total = 0m);
public static OrderState Apply(OrderState state, object evt) => evt switch
{
OrderPlaced e => state with { Status = "Pending", Total = e.Total },
OrderShipped _ => state with { Status = "Shipped" },
OrderCancelled _ => state with { Status = "Cancelled" },
_ => state
};
// Fold events to get current state:
var state = events.Aggregate(
new OrderState(id),
Apply);การตรวจสอบอย่างรวดเร็ว
นิพจน์ 'with' ทำอะไรกับเรคคอร์ดเดิม
สรุป: ความไม่เปลี่ยนแปลงด้วย init และ with
ประเด็นสำคัญ:
- ตัวเข้าถึง
init: กำหนดค่าได้เฉพาะระหว่างการเริ่มต้น และไม่สามารถกำหนดได้อีกหลังจากนั้น - คุณสมบัติแบบระบุตำแหน่งของเรคคอร์ดใช้
initเท่านั้นโดยค่าเริ่มต้น - นิพจน์
with: สร้างสำเนาที่ปรับเปลี่ยนแล้ว โดยเรคคอร์ดเดิมไม่เปลี่ยนแปลง - เชื่อมโยงนิพจน์
withสำหรับการแปลงหลายขั้นตอน - ชนิดที่ไม่เปลี่ยนแปลงปลอดภัยต่อเธรดโดยไม่ต้องซิงโครไนซ์
- ใช้ร่วมกับ
ImmutableList<T>เพื่อสร้างกราฟออบเจ็กต์ที่ไม่เปลี่ยนแปลงอย่างสมบูรณ์
คำถามที่พบบ่อย
บทเรียน “ความไม่เปลี่ยนแปลงด้วย init และ with” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ความไม่เปลี่ยนแปลงด้วย init และ with” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ความไม่เปลี่ยนแปลงด้วย init และ with”
ใช้ตัวกำหนดค่าแบบ init เท่านั้นเพื่อสร้างออบเจ็กต์ที่ไม่เปลี่ยนแปลง และใช้นิพจน์ with เพื่อสร้างสำเนาที่แก้ไขโดยไม่ทำลายออบเจ็กต์เดิม คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “ความไม่เปลี่ยนแปลงด้วย init และ with” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ชนิด Record: พื้นฐานและไวยากรณ์
- ความไม่เปลี่ยนแปลงด้วย init และ with
- ความเท่ากันของค่าและการแยกโครงสร้าง
- Records ในการออกแบบที่ขับเคลื่อนด้วยโดเมน