[C#] Arithmetic Overflow กับคำสั่ง checked และ unchecked

พอดีช่วงนี้ลองทวนสอบ เพื่อเตรียมสอบ Cert MCSD App Builder ของ Microsoft ครับ แล้วบังเอิญไปเจอ keyword อันนึงที่ กูรเขียน Code มาหลายปี ไม่เคยได้ยินเลย checked และ unchecked ครับ โดยก่อนเข้าไปรู้ว่า 2 คำนี้ มันมีความหมายอย่างไรใน C# เรามารู้จักกับ Exception ชนิดหนึ่งกันก่อนดีกว่าครับ

Arithmetic Overflow/Underflow คือ อะไร ?

สำหรับคอมพิวเตอร์เวลามันทดอะไรบางอย่าง เราจะเก็บที่หน่วยความจำ แล้วที่นี่ในการเขียนโปรแกรมตอนที่เราจะทดค่าต่างๆนั้น มันมีภาชนะให้เก็บหลายรูปแบบ ซึ่งพื้นฐานของคอมพิวเตอร์มาจากคณิตศาสตร์ การที่เราจะเก็บอะไรลงภาขนะ แต่ละแบบ มันมีข้อจำกัด นัั่นก็ช่วง หรือขอบเขตที่สามารถรับได้ ถ้าสรุปในภาษา Dev มันบอกว่า Data Type(ภาชนะ) ซึ่งแต่ละแบบ เช่น int, float Decimal หรือ String มันมีข้อจำกัด หรือขอบเขต ที่บอกว่าเราสามารถเก็บอะไรลงไปได้ และได้เท่าไหร่ มี min-max นั้นเองครับ ถ้าลองดูใน C# ค่า min และ max ของ DataType บางชนิดมา มีค่า min/max นะครับ ดังนี้ครับ

short.MaxValue    :  32767
short.MinValue    : -32768
ushort.MaxValue   :  65535
ushort.MinValue   :  0
int.MaxValue      :  2,147,483,647
int.MinValue      : -2,147,483,648
uint.MaxValue     :  4,294,967,295
uint.MinValue     :  0
long.MaxValue     :  9,223,372,036,854,775,807
long.MinValue     : -9,223,372,036,854,775,808

แล้ว Overflow กับ Underflow  คือ อะไรหละ ?

  • Overflow คือ การคำนวณต่างๆ แล้วผลลัพธ์ที่ได้มันมีค่ามากกว่าที่ตัวแปรชนิดนั้นๆ จะเก็บค่าได้ครับ (สั้นๆ ค่าเกิน max)
  • Underflow คือ การคำนวณต่างๆ แล้วผลลัพธ์ที่ได้มันมีค่าน้อยกว่าที่ตัวแปรชนิดนั้นๆ จะเก็บค่าได้ครับ (สั้นๆ ค่าน้อยกว่า min)

OverflowException คือ อะไร ?

Exception ข้อผิดพลาด ที่เราสามารถจัดการได้ สำหรับในกรณีนี้ คือ ข้อมูลที่คำนวณได้ มันล้นเกิน(Overfllow) ออกมาจากจากภาชนะที่เก็บไว้ (ตัวแปร) ถ้าดูโครงสร้าง OverflowException  พบว่ามันเป็น Type หนึ่งของ System.ArithmeticException ครับ

checked & unchecked

หลังจากรู้จักกับ Arithmetic Overflow Exception แล้ว กลับมาที่ตัว C#กันก่อนครับ ว่าตัวภาษามันมีอะไรช่วยเรา หรือป่าว คำตอบ คือ มีครับ แต่ปกติไม่เปิดไว้ (ค่า Default = unchecked) แต่มีคำสั่งเข้ามาจัดการเรื่องนี้ 2 คำสั่งครับ ได้แก่

  • checked - บอกว่า Code ที่อยู่ในส่วนของ คำสั่ง checked มีการตรวจสอบ พวก Overflow/Underflow ครับ ถ้าดูจาก Code เป็น Code ที่ผมใช้คำนวณดอกเบี้ยของพันธบัตร/หุ้นกู้(Bond) ครับ
public class BondCalculation
{
    //Some Business Logic

    public Decimal CalcInterest()
    {
        checked
        {
            /*
                Some Interest Calc Logic
                ...
                Some Interest Calc Logic
            */
            return Interest
        }
    }
}
  • unchecked - บอกว่า Code ที่อยู่ในส่วนของ คำสั่ง unchecked ไม่มีการตรวจสอบ พวก Overflow/Underflow ครับ อ้าวแล้วกรณีไหนที่ต้องไม่ Check หละ การคำนวณด้าน การเงิน คงไม่ใช่แน่ๆ เงินหาย คงไม่มีใครยอมอยู่แล้วครับ แต่มีบางอัลกอริทึมนะ ที่ไม่ได้สนใจค่าของมัน(magnitude) อย่าง เช่น การทำ Hash ครับ
[Serializable]
public abstract class MasterDTO : RepositoryDTO
{
    //Some Logic

    public override int GetHashCode()
    {
        unchecked
        {
            //Sample Logic
            int hashCode = 17;
            hashCode = hashCode * 19 + (Id == null ? 0 : Id.GetHashCode());
            hashCode = hashCode * 19 + (CreateTime == null ? 0 : CreateTime.GetHashCode());
            //Sample Logic
            //Note: your can add you logic for calculating hash code
            return hashCode;
        }
    }
}

Note:

  • สำหรับตอนนี้ใน C# ยังไม่มี UnderflowException ครับ ถ้าเกิด Exception ในกรณีนี้จริงๆ ระบบจะโยนตัว InvalidOperationException มาแทนครับ
  • คำสั่ง checked / unchecked  มี ขอบเขตแบบ Local ถ้าในขอบเขตดันไปมีการเรียกใช้ Method อื่น ตัว Method อื่น ไม่เข้าข่ายนะครับ ตามตัวอย่าง พบว่า Method CalcSomethingOverflows ไม่เข้าขอบเขตของคำสั่ง checked ครับ
public class TestChecked
{
    public void TestOverflow()
    {
        checked
        {
            // this method will execute in unchecked context
            CalcSomethingOverflows();
        }
    }

    public void CalcSomethingOverflows()
    {
        // no overflow exception chekcibng (thrown)...
        var overflowed = Decimal.MaxValue + 1;
    }
}
  • คำสั่ง try-catch มันก็ไม่ได้สนใจพวก Overflow และ Underflow ครับ โดยจากตัวอย่าง Code สังเกตุว่า Method dependsonDefault ปล่อยให้คำตอบเป็นไปตามยถากรรมครับ ซึ่งสามารถทำงานได้ด้วย แต่ถูก หรือป่าวอีกเรื่องนะครับ แต่หากมีคำสั่ง Checked ไว้ ตามใน Method checkedMethod มันจะ throw exception ทันทีครับ
using System;
class Test
{
   static readonly int x = 1000000;
   static readonly int y = 1000000;
   static int checkedMethod() {
      return checked(x * y);      // Throws OverflowException
   }
   static int uncheckedMethod() {
      return unchecked(x * y);   // Returns -727379968
   }
   static int dependsonDefault() {
      return x * y;               // Depends on default
   }
   
   public static void Main()
   {
      Console.WriteLine();
      try
      {
         Console.WriteLine("Call uncheckedMethod - Can execute and not throw exception");
         Console.WriteLine(Test.uncheckedMethod());
         Console.WriteLine("==========================================");
         Console.WriteLine("Call dependsonDefault - Can execute and not throw exception");
         Console.WriteLine(Test.dependsonDefault());
         Console.WriteLine("==========================================");
      }
      catch(Exception ex)
      {
         Console.WriteLine(ex);
      }

      Console.WriteLine("Call checkedMethod - Can not execute and throw exception");
      Console.WriteLine(Test.checkedMethod());
      Console.WriteLine("==========================================");
   }
}
  • ผลลัพธ์การ Run ของ Code ตัวอย่าง try-catch ครับ

โห ถ้าจะต้องการให้มันตรวจสอบทั้ง Project นีไม่ต้องไล่ใส่คำสั่ง Checked ไปจนพรุนทั้ง Solution/Project เลยเหรอ คำตอบ คือ ว่ามีครับ ถ้าให้ในมัน Defaultให้มีค่าเป็น checked ตั้งแต่ต้นให้ไปกำหนดค่าใน Project ดังรูปครับ

ถ้าตรงไหนไม่เอา ก็สามารถใช้คำสั่ง unchecked มากำหนดแทนครับ

Reference


Discover more from naiwaen@DebuggingSoft

Subscribe to get the latest posts sent to your email.