บทความและข่าวสาร | Seven Peaks Insights

แนะนำการใช้งาน C# เวอร์ชันใหม่

พื้นฐานของ C# และการพัฒนาด้วย RabbitMQ

C-Developing-with-RabbitMQ

 

พื้นฐานของ C# – ย้อนดูประวัติของ C# ตั้งแต่เวอร์ชันแรก

เรามาเริ่มพื้นฐานความรู้เกี่ยวกับ C# ด้วยการย้อนไปที่เวอร์ชันเก่าที่สุดตั้งแต่ C# 1.0 กันก่อน มันเป็นเวอร์ชันแรกที่ออกเผยแพร่เมื่อเดือนมกราคม ปี 2002 ซึ่งเป็นเพียงเวอร์ชันที่มีเพียงฟังก์ชันพื้นฐานเท่านั้น จากนั้นก็พัฒนาต่อมาเป็นเวอร์ชัน 2.0 ในอีก 3 ปีให้หลังในเดือนพฤศจิกายน ปี 2005 ซึ่งถือว่าเป็นอัปเดตใหญ่ที่มีฟีเจอร์ดีๆ อย่าง generics และ nullable types และฟีเจอร์อื่นๆ ที่มีประโยชน์ จากนั้นอีก 2 ปีต่อมาก็มีเวอร์ชัน 3.0

อีก 3 ปีต่อมา C# ก็ออกเวอร์ชัน 4.0 ที่เพิ่ม generics covarian/contravariant เข้ามา ซึ่งก็เป็นอีกฟีเจอร์ที่มีประโยชน์ จากนั้น C# 5 ก็ออกตามหลังมาอีก 2 ปี พร้อมด้วยฟีเจอร์ asynchronous ที่เรานำมาใช้ใน .NET framework เวอร์ชัน 4.5 หลังจากนั้น ในปี 2015 เราก็มี .NET Core เวอร์ชันแรกให้ใช้พร้อมๆ กับเวอร์ชัน 6.0

ในเวอร์ชันนี้บรรจุฟีเจอร์ใหม่ๆ ที่มีประโยชน์มากมาย เช่น string interpolation ซึ่งแม้จะดูเหมือนเป็นฟีเจอร์ธรรมดาๆ แต่มีประโยชน์มาก จากนั้นก็มีเวอร์ชันย่อยของ 7.0 ตามด้วยเวอร์ชัน 8.0 ที่มาพร้อมกับ .NET Core 3.0 และ Visual Studio 2019 ที่เราใช้ในการพัฒนาซอฟต์แวร์ของเรา

 

ฟีเจอร์ของ C# เวอร์ชัน 9.0

C# เวอร์ชันล่าสุดคือเวอร์ชัน 9.0 และยังเป็นเวอร์ชัน preview อยู่ ในเวอร์ชันนี้ C# ตั้งเป้าว่าจะทำให้โค้ดสั้นลงและสร้าง immutable object ด้วยหลักการ OOP (object-oriented programming) โดยคุณสามารถเริ่มเรียนรู้ C# ที่ใช้ boilerplate code น้อยลงได้ เนื่องจากมันเข้าใจง่ายขึ้นว่ามีอะไรเกิดขึ้นบ้างและดูแลง่ายด้วย

Target typing ช่วยให้การเขียนโค้ดสั้นลงเมื่อคุณต้องการสร้าง instance ใหม่ขึ้นมาในโปรเจกต์

สำหรับฟีเจอร์ Init-only นั้น C# เวอร์ชันนี้พยายามที่จะใช้ immutable data มากขึ้น ดังนั้นจึงสามารถแสดงข้อมูลได้โดยไม่จำเป็นต้องปรับแต่งอะไรเพิ่มเติม ซึ่งค่อนข้างมีประโยชน์ถ้าหากคุณต้องการเก็บรักษาประวัติและข้อมูลแวดล้อมเอาไว้ ฟีเจอร์อีกอย่างที่ดีก็คือ records ซึ่งเป็น immutable สำหรับทั้ง object ไม่ใช่แค่ property

Top Level Statements

ใน C# เวอร์ชันก่อนหน้านี้ การเขียนภาษาโปรแกรมที่เรียบง่ายนั้นจำเป็นต้องใช้ boilerplate code จำนวนมหาศาล แต่ในเวอร์ชันใหม่อย่าง C# 9.0 พวกเขาได้ทำให้โค้ดสั้นลงและทำงานเหมือนเดิม สำหรับมือใหม่ที่เพิ่งเริ่มหัดพื้นฐาน C# ก็สามารถเลือกเขียนโปรแกรมในรูปแบบนี้ง่ายขึ้น โดยคุณไม่ต้องระบุ type ก็ได้หาก type นั้นชัดเจนอยู่แล้ว

ลองพิมพ์โค้ดนี้:

using System;
Console.WriteLine(“Hello World!”)

 

Target-typed

New expressions:

ด้วยความที่ C# 9.0 ใช้ new expressions ทำให้คุณไม่ต้องระบุ type หลัง keyword ใหม่อีกต่อไป

ลองพิมพ์โค้ดนี้:

//C# 9.0 Person person = new(“Prayuth”, “Sudjai”);
var people = new Person[]
{
new (),
new (“Donald”, “Duck”),
new () { FirstName = “Taylor”, LastName = “Swift” },
};

Conditional expressions

Expressions นั้นปกติแล้วจะไม่ใช้ type ร่วมกันในแต่ละ branch แต่ใน C# 9.0 นั้นอนุญาตให้ใช้ target type ร่วมกันระหว่าง branch หลายอันได้

ลองพิมพ์โค้ดนี้:

public class Person {}
public class PrimeMinister : person {}
public class President : person {}
Null-Coalescing operator (??) works with common base type
Person person = president ?? primeMinister;
Ternary operator (?:) works with nullable value type
int? result = notnull ? 0 : null;

Covariant returns

ก่อนหน้านี้ signature ของ method ที่คุณต้องการ return นั้นจำเป็นต้องเป็น type เดียวกับ base class ข้อดีก็คือ ตอนนี้คุณสามารถ express ว่า method มี return type ที่เฉพาะเจาะจงกว่าอันที่อยู่ใน base type ได้

ลองพิมพ์โค้ดนี้:

//person.cs
public virtual Person GetPerson()
{
return new Person();
}
//president.cs
public override President GetPerson()
{
// no need casting!
return new President();
}

ฟีเจอร์ Init-only

ช่วยให้ client มีฟอร์แมตที่อ่านง่ายในการสร้าง object โดยคุณสามารถตั้งค่าได้แค่ครั้งเดียวเมื่อมันเริ่มการทำงานของ constructor

ลองพิมพ์โค้ดนี้:

public class Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public Person(string firstName = null,
string lastName = null) =>
(FirstName, LastName) = (firstName, lastName);
}

Records

Records นั้นจะมีประโยชน์ถ้าหากว่าคุณต้องการเก็บประวัติการใช้ข้อมูล เช่น sourcing และ object value เอาไว้ ซึ่ง records สามารถทำงานร่วมกับ expressions ได้ โดยคุณสามารถใช้ with-expression เมื่อคุณต้องการคัดลอก object ได้

นอกจากนั้น คุณยังสามารถกำหนดค่าใหม่ได้ ดังนั้น object ใหม่จะรับเอาข้อมูลจาก object เก่า พร้อมด้วยค่าใหม่ที่คุณกำหนดใน object initializer ที่จะส่งข้อมูลไปยัง object หลายๆ ตัว

ลองพิมพ์โค้ดนี้:

public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
public record Person(string FirstName, string LastName)

With-expressions

เจ้าสิ่งนี้ทำงานอยู่เบื้องหลัง records อย่างไร

คุณสามารถใช้มันร่วมกับ expressions ได้เมื่อคุณต้องการโคลน object ขึ้นมา ซึ่งมันจะทำงานอยู่เบื้องหลังด้วยการสร้าง protected constructor ที่คุณสามารถคัดลอกข้อมูลได้ทีละ field

ลองพิมพ์โค้ดนี้:

var newPerson = person with { LastName = “Alpha” };
protected Person(Person original) { /* copy all the fields */ } //
generated

Value-based equality

Value-based equality นั้นนำไปใช้ตรวจสอบว่า object นั้นมีค่าเท่ากันหรือไม่ และสำหรับ ReferenceEquals type นั้นจะนำไปใช้เพื่อระบุ ObjectParameter สองอันเมื่อทั้งสองพารามิเตอร์เป็นแบบ non-null แต่สำหรับ Equals คุณสามารถใช้ได้โดยไม่ต้องมี reference ซึ่งจะแสดงค่าที่แท้จริงของทั้งสอง object

ลองพิมพ์โค้ดนี้:

Object.Equals(object, object)
ReferenceEquals(person, newPerson) = false
Equals(person, newPerson) = true

Introducing-the-new-C-Developing-with-RabbitMQ

 

Messaging

นอกจากต้องใช้เวลาในการเขียนโค้ดใหม่ตั้งแต่ต้นแล้ว คุณอาจต้องเจอกับปัญหา เช่น technical debt หรือค่าบำรุงรักษาที่สูงเกินไป แต่ยังมีตัวเลือกอื่นที่ดีกว่าสำหรับธุรกิจของคุณนั่นก็คือ เราแยกแอปพลิเคชันแบบ monolith เป็น service ย่อยๆ แทน
 

ทำอย่างไรให้ service เหล่านี้เชื่อมต่อกันและสื่อสารกันอย่างมีประสิทธิภาพมากยิ่งขึ้น?

การเชื่อม service เหล่านี้เข้าด้วยกันตามแนวทาง decoupling นี่เองคือเหตุผลว่าทำไมถึงมีการสร้าง messaging queue platform ขึ้นมา ประโยชน์ของมันก็คือเราสามารถ decouple แอปพลิเคชันออกเป็น service ย่อยๆ ตามความต้องการของธุรกิจได้

ด้วยวิธีดังกล่าว เราสามารถปรับขยาย service ได้หากเรามีหลาย service ซึ่งจะมีเพียงบาง service ที่ถูกนำไปประมวลผล ดังนั้น เราก็สามารถปรับเพิ่มหรือลด service นั้นๆ ได้ตามต้องการ นอกจากนี้ เรายังทำให้ UX ออกมาดีขึ้นอีกด้วยเพราะว่า task จำนวนมากสามารถประมวลได้พร้อมๆ กัน ซึ่งทำให้ในฝั่งของ client นั้น ลูกค้าไม่จำเป็นต้องรอจนมันประมวลผลเสร็จ

 

ทำไมต้องใช้ RabbitMQ?

มีผู้ให้บริการ messaging platform มากมาย เช่น Apache Kafka, Azure Service Bus, Google Cloud Pub/Sub, RabbitMQ และอื่นๆ ซึ่งเราจะอธิบายเกี่ยวกับ RabbitMQ ให้คุณเข้าใจ เนื่องจากนี่เป็นหนึ่งในบริการ messaging ที่ประสบความสำเร็จมากที่สุด

สำหรับ Azure Service Bus หรือ Google Cloud Pub/Sub นั้น คุณต้องโฮสต์มันไว้ที่ Azure หรือ Google Cloud ซึ่งจะทำให้การย้ายข้อมูลจากผู้ให้บริการคลาวด์หนึ่งไปยังอีกที่เป็นเรื่องยาก RabbitMQ นั้นเป็นแพลตฟอร์มที่มีฟังก์ชันครบครันและผ่านการพัฒนามาอย่างยาวนาน มี client library มากมายที่รองรับหลากหลายภาษา จึงสามารถรองรับภาษาส่วนใหญ่ได้ ไม่ใช่แค่ C#, Java, และ Python เท่านั้น

การทำงานขั้นพื้นฐาน

Advanced Message Queueing Provider (AMQP)

AMQP คือโปรโตคอลที่พัฒนาขึ้นมาเพื่อนำไปใช้กับธุรกิจด้านการเงินเพื่อสนับสนุนการทำงานของ AMQP 0-9-1

Queue

Queue คือสิ่งที่เอาไว้เก็บ message ต่างๆ ซึ่งมี publisher ในฝั่งหนึ่ง ส่วนอีกฝั่งเป็น consumer เพื่อรับ message แล้วนำไปประมวลผล

Exchange and Binding

Exchange นั้นทำหน้าที่ตัดสินใจว่าควรทำอย่างไรกับ message ซึ่ง exchange นั้นจะอยู่ระหว่าง publisher กับ queue เมื่อ publisher ส่ง queue ไปยัง exchange แล้ว exchange ก็จะตัดสินใจว่า queue ไหนที่จะถูกส่งไปด้วยการกำหนดค่าที่เรียกว่า binding
 

Exchange types

Fanout Exchange

Fanout exchange ทำหน้าที่คัดลอกและแจกจ่าย message ไปยัง queue ทั้งหมดที่ทำการ bind เอาไว้อย่างเท่าเทียมกันไม่ว่า message นั้นจะเกี่ยวกับอะไรก็ตาม

Direct Exchange

สำหรับ direct exchange นั้น publisher จำเป็นต้องระบุ routing key ใน message และเราจะต้องตั้งค่า routing key เอาไว้ล่วงหน้าด้วย

ในตัวอย่างนี้ Q1 จะ listen message ใดๆ ก็ตามที่ binding key ตรงกับสีส้ม และ Q2 จะ listen message ใดๆ ก็ตามที่ตรงกับสีดำและเขียว

Topic Exchange

Topic exchange เป็น exchange type ที่ได้รับความนิยมสูงสุด คล้ายกับ direct exchange ที่เรายังคงใช้ routing key แต่มีฟีเจอร์ในด้าน topic type เราสามารถใช้สัญลักษณ์ * และ # เป็น wildcard ได้ โดยใช้ * แทนคำหนึ่งคำพอดี และ # แทนคำจำนวนศูนย์คำขึ้นไป

Header Exchange

Header exchange ใช้ attribute ของ message header สำหรับ routing โดยมันจะแจกจ่าย message ตาม argument ที่ได้รับมา ซึ่งมีข้อมูลของ header และค่าอื่นๆ รวมอยู่

คุณต้องการความช่วยเหลือในการพัฒนา back-end หรือไม่
ติดต่อเราเพื่อดูว่าเราสามารถช่วยอะไรคุณได้บ้าง
ติดต่อเรา