protect

ถ้าเอาบทความไปเผยแพร่แล้วไม่ให้เครดิต ดำเนินคดีด้วย พรบ. คอมพิวเตอร์ฉบับใหม่ ขั้นสูงสุดและไม่ยอมความครับ

วันพุธที่ 11 พฤศจิกายน พ.ศ. 2558

Assembly Module : ตอนที่ 6 manifest table


ก่อนอื่นขออนุญาติออกตัวก่อนน่ะครับ ช่วงนี้ที่ทำงานผมเริ่มกลับมามีงานแล้วครับ จากที่ สองสามเดือนก่อนหน้าไม่ค่อยมีงาน ตอนนี้เลยไม่ค่อยมีเวลาว่าง อาจจะออกบทความช้าหน่อยน่ะครับ

ใน header ของ metadata ตอนแรกผมตั้งใจจะเขียน definition table จากนั้นจะเขียนเรื่อง reference table แล้วค่อยเขียนเรื่อง manifest table หลังจากที่เริ่มเขียน reference table กลายเป็นว่ามันเกี่ยวเนื่องกันกับ manifest table คือต้องมีความเข้าใจในเรื่อง manifest table ก่อน จึงจะเข้าใจว่าอะไรคือ reference table ดังนั้นบทความนี้ผมจะอธิบาย manifest table น่ะครับ

โดยปกติโปรแกรมที่เราเขียนจึ้นมาทั้งหมดสามารถ execute ได้เพราะว่า มี manifest table และทำให้ execute file กลายเป็น Assembly module ขณะทำ runtime โดยทุกๆ code ที่ถูก compile ด้วย Visual Studio จะถูก compiler สร้าง manifest ไว้ที่ execute table ทุกครั้ง หมายความว่า code ที่ถูก compile ด้วย Visual Studio จะเป็น Assembly module เสมอ

ทุกๆ Assembly module ไม่ว่าจะเป็น static หรือ dynamic จะทำหน้าที่เก็บข้อมูลของ file ต่างๆ ที่ assembly เรียกใช้รวมถึงอธิบายวิธีการใช้ข้อมูลนั้น ข้อมูลของ file ในทีนี้สามารถเป็นได้ในรูปแบบของรูปภาพ (jpg หรือ gif), file library ที่มีนามสุล DLL และถูกเรียกใช้โดย module นอกจากจะเก็บข้อมูลของ file แล้วยังเก็บข้อมูล ดังต่อไปนี้

Assembly name - ชื่อของ assembly เก็บข้อความเป็นตัวหนังสือ
Version number - version ของ assembly
Culture -ข้อมูลเกี่ยวกับภาษาที่ assembly รองรับ
Strong name - หัวข้อนี้จะกล่าวในบทความถัดไปครับ
Type reference - ข้อมูลเกี่ยวกับ Type ที่ Assembly มีการไป reference มาจาก module อื่น ข้อมูลส่วนนี้ จะเชื่อมไปยัง table ModuleRef และ TypeRef (อยู่ในกลุ่มของ reference table ซึ่งจะกล่าวในหัวข้อถัดถัดไป)
Information on reference assemblies - ข้อมูลเกี่ยวกับ Assembly อื่นที่ Assembly นี้มีการไป reference โดยข้อมูลส่วนนี้จะมีการเชื่อมไปยัง table AssemblyRef (อยู่ในกลุ่มของ reference table ซึ่งจะกล่าวในหัวข้อถัดถัดไป)

โดยข้อมูลทั้งหมดที่ผมอธิบายจะเก็บในรูปแบบของ table เราเรียก table ในส่วนนี้ว่า manifest table

เมื่อโปรแกรมชนิด managed ถูกทำงาน สิ่งแรกที่ runtime ทำงานก่อนคือ อ่านข้อมูลของ manifest table เพื่อที่จะทราบรายละเอียดของ Type ที่ module เรียกใช้และจำขนาดของหน่วยความจำตามที่ข้อมูล manifest กำหนด ขนาดของหน่วยความจำนี้สำคัญมากตอน runtime ทำการ compile IL code ไปเป็น native code เพราะเป็นขั้นตอนที่จะจองหน่วยความจำของตัวเครื่องจริงๆ

ในตอนหน้าเราจะมาดูรายละเอียด table ของ manifest table กันครับ
พบกันใหม่บทความหน้า สวัสดีครับ
TuChay

วันศุกร์ที่ 30 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 5 TypeDef table และสมาชิก (3)


เรามาต่อจาก บทความที่แล้วกันเลย ด้วยตัวอย่าง code จากบทความที่แล้ว ผมแสดงให้ดูถึงรายละเอียดของ Table TypeDef ไปแล้วในบทความนี้จะมาพูดถึงรายละเอียดของ table อื่นๆ ที่สัมพันธ์กับ table TypeDef กันครับ

จาก execute file ที่ได้จากตัวอย่างบทความที่แล้ว ถ้าเราเปิดด้วย ILDasm แล้วไปที่เมนู View -> MetaInfo -> Show!

ลองเลื่อนรายละเอียดของ metadata มาจนถึงรูปข้างล่างครับ



Method #1 (06000001)  จะเป็นสมาชิกของ table TypeDef
   #1 จะหมายถึง Index ของ method แล้วจะมีค่าบวกไปเรื่อยๆ สำหรับ method ถัดไป
   06000001 โดยที่ตัวเลข 06 ที่อยู่ข้างหน้า เป็น reference code สำหรับ runtime เพื่อให้ runtime ทราบขณะกำลัง compile ว่านี้คือ table MethodDef แน่นอนครับตัวเลขที่เป็นตัวเลขฐาน 16

ส่วนรายละเอียดข้างล่าง ของ Method #1 (06000001) ผมจะอธิบายเป็นบางส่วนก่อน ส่วนที่ยังไม่อธิบายจะไปอธิบายในบทความถัดๆ ไปน่ะครับ

MethodName 
จากตัวอย่างในรูปคือ .ctor หมายถึงเป็น constructor ก็คือมีชื่อเดียวกับชื่อของ TypeDef

Flags
บอกให้รู้ว่า method นี้มี flags อะไรบ้าง เช่น public (ส่วน flags ชนิดอื่น ผมจะอธิบายในส่วนถัดไปน่ะครับ)

RVA (Relative Virtual Address)
เป็น address ที่เก็บ IL code ของ method นี้ หมายความว่าเมื่อ runtime ทำงาน runtime จะไป compile IL code ของ method ที่ address 0x00002050 (ยังจำได้ไหมครับ managed execute file แบ่งออกเป็น 4 ส่วน สามารถย้อนไปอ่านได้ที่นี้ครับ)

ImplFlags
เป็น falgs ที่บอกว่า method นี้ถูก implement มาอย่างไร ในตัวอย่างหมายความว่า method นี้เป็นชนิด IL และสามารถ manage ได้

ReturnType
จากตัวอย่างเป็น Void คือ ไม่มีค่า return กลับ

No arguments
ไม่มีพารามิเตอร์สำหรับ method นี้ 

ที่นี้เราลองมาดู method Main กันบ้างน่ะครับ




 ใน method Main จะสังเกตุเห็นคำว่า [ENTRYPOINT] ซึ่งเป็นตัวบอก runtime ว่านี้เป็น method แรกเมื่อมีการ execute โปรแกรม 
RVA : 0x000020c2 บอกให้รู้ตำแหน่งของ IL code ของ entrypoint 
1 Arguments คือ method นี้ต้องการ 1 พารามิเตอร์ โดยมี Type SZArray ที่เก็บค่า string ของ Array
Signature : 00 01 01 1d 0e เป็น index ที่ชี้ไปที่ table ParamDef 
1 Parameters เป็นรายละเอียดของ table ParamDef ที่เกี่ยวกับ parameter นี้ จำนวน parameter จะเท่ากับจำนวน Arguments เสมอ

 พบกันใหม่บทความหน้าครับ
TuChay 

วันพฤหัสบดีที่ 29 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 5 TypeDef table และสมาชิก (2)


ตอนที่แล้วผมแนะนำรายละเอียดของ table ต่างๆ ที่อยู่ในส่วนของ Definition table ไปแล้ว ที่นี้เราลองมาดูเนื้อหาของ table จากตัวอย่าง code C# ดูน่ะครับ

ลองดูตัวอย่าง code ง่ายๆ น่ะครับ

using System;

namespace CTS
{
    class Program
    {
        public int thisIsField;
        public event EventHandler thisIsEvent;
        public Program()
        {

        }

        ~Program()
        {

        }

        public string thisIsProperty
        {
            get { return ""; }
            set {}
        }


        public void thisIsMethod()
        {

        }

        public void thisIsMethod(int param)
        {

        }

        public string this[int inderxer]
        {
            get { return ""; }
            set { }
        }

        static void Main(string[] args)
        {
         
        }
    }

    public class ThisIsSecondType
    {
        public ThisIsSecondType()
        {

        }
    }
}


ตัวอย่าง code นี้มาจาก code ของ Common Type System (CTS) ตอนที่ 2  แต่ผมเพิ่มเข้าไปอีกหนึ่ง Type คือ ThisIsSecondType จากนั้น compile code เพื่อที่จะได้ execute file แล้วเปิด execute file ที่ได้ด้วย ILDasm น่ะครับ จากนั้นไปที่เมนู View -> MetaInfo -> Show!

เรามาดู information ของ table TypeDef ก่อน รูปข้างล่างเป็นตัวอย่างรายละเอียดของ table TypeDef




ที่บรรทัด TypeDef #1 (02000002)
   #1 จะเป็น index ของ Type โดยตัวเลขที่เพิ่มขึ้นไปทีล่ะหนึ่งสำหรับ Type ถัดไป
  02000002 ตัวเลข 02 ที่อยู่ข้างหน้า เป็น reference code สำหรับ runtime เพื่อให้ runtime ทราบขณะกำลัง compile ว่านี้คือ table TypeDef  โดย table TypeDef จะ start ที่ 0x02 ตัวแรก 0x หมายถึงเลขฐาน16 หมาความว่าตัวเลข 02000002 เป็นตัวเลขฐาน 16 น่ะครับ

ดังนั้นตัวเลข 02000010 ฐาน 10 จะมีค่าไม่เท่ากับ 02000010 ฐาน 16 

รายละเอียดข้างล่างอีก 3 บรรทัดจะเป็นรายละเอียดของ Type
    TypeDefName เป็นชื่อของ Type โดยชื่อของ Type จะรวม namespace หน้าชื่อของ Type ใน C# code
    Flags เป็นตัวบอกชนิดของ Type จะสังเกตุว่าใน 1 Type สามารถมีได้หลาย flag
    Extends เป็นตัวบอกให้รู้ว่า Type นี้มีการสืบถอดมาจาก Type ไหนบ้าง

อันนี้สำคัญน่ะครับ Type ทุก Type ที่เขียนขึ้นภายใต้ .Net Framework จะถูกสืบทอดมาจาก Type System.Object เสมอครับ นั้นคือเหตุผลว่าทำไม Type ทุก Type ที่เราสร้างขึ้นจึงมี method ToString() ทุกครั้ง

ที่นี้ลองมาดู Type ThisIsSecondType ใน code น่ะครับ จะเป็นได้ดังรูปข้างล่าง



สิ่งที่ต่างจาก Type Program (Type แรกของ table TypeDef) คือ Index ของ Type เป็นหมายเลข 2 และเลข reference เป็น 0x02000003 (จริงๆ ก็คือ 02000003 แหละครับ แต่ใส่ 0x เพื่อย้ำให้รู้ว่านี้เป็นเลขฐาน 16)

Flags ของ Type ThisIsSecondType เราระบุว่าเป็น public ในขณะที่ Type Program เราไม่ได้ระบุดังนั้น C# compiler จะให้ Type Program มี flag เป็น NotPublic เป็นค่า default

พบกันใหม่บทความหน้าครับ
TuChay




วันอังคารที่ 27 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 5 TypeDef table และสมาชิก (1)


TypeDef table เป็น table ที่อยู่ในส่วนของ Definition table มี reference code 0x02 ตอนทำ runtime (reference code ผมจะอธิบายพร้อมยกตัวอย่างในตอนถัดไปครับ) ใน 1 Module จะมี TypeDef แค่ 1 table เท่านั้น ทำหน้าที่เก็บรายละเอียดของ Type ที่เป็นสมาชิกของ module โดยแต่ล่ะแถวของ table จะเก็บข้อมูลของ Type แต่ล่ะชนิด ข้อมูลในแถวจะเก็บรายละเอียดของข้อมูลดังนี้

1) ชื่อของ Type
2) base type ที่มีการสืบทอดมา
3) flags access modifiers ของ Type (เช่น public, private)
4) Index ของ method ที่ Type นี้เป็นเจ้าของ ข้อมูลของ method จะอยู่ใน table MethodDef โดยมี index นี้เป็นตัวเชื่อมระหว่าง TypeDef กับ MethodDef
5) Index ของ field ที่ Type นี้เป็นเจ้าของ ข้อมูลของ field จะอยู่ใน table FieldDef โดยมี index นี้เป็นตัวเชื่อมระหว่าง TypeDef กับ FieldDef
6) Index ของ property ที่ Type นี้เป็นเจ้าของ ข้อมูลของ property จะอยู่ใน table PropertyDef โดยมี index นี้เป็นตัวเชื่อมระหว่าง TypeDef กับ PropertyDef
7) Index ของ event ที่ Type นี้เป็นเจ้าของ ข้อมูลของ event จะอยู่ใน table EventDef โดยมี index นี้เป็นตัวเชื่อมระหว่าง TypeDef กับ EventDef

จากคำอธิบายข้างต้นเราจะเห็นความสัมพันธ์ของ database อย่างง่ายๆ โดยมี TypeDef เป็น table หลักและมี table อื่นๆ เป็นตัวเก็บรายละเอียดของ Type

เรามาดู table ย่อยที่มีความสัมพันธ์กับ TypeDef โดยมีรายละเอียดดังนี้

1) MethodDef
    เป็น table ที่เก็บรายละเอียดของ method ที่เป็นสมาชิกของ Type มี reference code 0x06 ตอนทำ runtime โดยรายละเอียดที่เก็บใน table มีดังนี้
   - ชื่อของ method
   - flags access modifiers ของ method (เช่น public, private)
   - Index ของ parameter ของ method (signature) ข้อมูลของ parameter จะอยู่ใน table ParamDef โดยมี index นี้เป็นตัวเชื่อมระหว่าง TypeDef กับ ParamDef

2) ParamDef
    เป็น table ที่เก็บรายละเอียดของ parameter ของ method  มี reference code 0x08 ตอนทำ runtime ใน 1 แถวจะเก็บรายละเอียดของ พารามิเตอร์ที่ถูกกำหนดใน method โดยข้อมูลในแต่ล่ะแถวจะเก็บรายละเอียดดังนี้
    - Type ของ parameter
    - ชื่อของ parameter
    - flag ของ parameter เช่น in, out, retval

2) FieldDef
  เป็น table ที่เก็บรายละเอียดของ filed ที่เป็นสมาชิกของ Type มี reference code 0x04 ตอนทำ runtime โดยรายละเอียดที่เก็บใน table มีดังนี้
    - Type ของ field
    - ชื่อของ field
    - flag ของ field เช่น private, public

3) PropertyDef
    เป็น table ที่เก็บรายละเอียดของ property ที่เป็นสมาชิกของ Type มี reference code 0x17 ตอนทำ runtime โดยรายละเอียดที่เก็บใน table มีดังนี้
    - Type ของ property
    - ชื่อของ property
    - flag ของ propertyเช่น private, public


4) EventDef

    เป็น table ที่เก็บรายละเอียดของ event ที่เป็นสมาชิกของ Type มี reference code 0x14 ตอนทำ runtime โดยรายละเอียดที่เก็บใน table มีดังนี้
    - Type ของ event
    - ชื่อของ event
    - flag ของ event เช่น private, public


ในตอนหน้าเราจะมาดูวิธีการอ่านรายละเอียดของ table TypeDef และ table อื่นๆ ที่ TypeDef มีการอ้างถึงกันครับ

พบกันใหม่บทความหน้าครับ
TuChay


วันจันทร์ที่ 19 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 5 ModuleDef table


Table แรกและเป็น table ที่สำคัญในส่วนของ Definition table คือ ModuleDef เป็น table ที่เก็บรายละเอียดของ module ได้แก่

 1) ชื่อของ module และนามสกุลของ module
เราสามารถแก้ไขชื่อและนามสกุลในส่วนของ table ModuleDef ได้แต่เนื่องจากมีหลาย table ใน metadata ทำการ link ไปที่ข้อมูลของ ModuleDef เพื่อป้องกันความผิดพลาดในการทำ runtime จึงไม่ควรเปลี่ยนชื่อและนามสกุลใน ModuleDef โดยเด็ดขาด

2) MVID - Module Version Identifier
 จะเป็นค่า uniqe ที่จะไม่ซ้ำกันและค่าจะเปลี่ยนไปเรื่อยๆ ทุกๆ ครั้งที่มีการ compile code แม้แต่ code เดียวกันถ้า compile code 2 ครั้ง ค่า MVID ทั้งสองครั้งก็จะมีค่าไม่เหมือนกัน รูปแบบของ MVID จะเป็นรูปแบบเดียวกับ GUID โดยมีการ checksums เพื่อนำมาสร้างค่า MVID

ที่ .Net Framework มีเปลี่ยนแปลงค่า MVID ทุกครั้งที่มีการ compile code เป็นเพราะว่า โดยปกติบริษัทที่จำหน่าย software มักจะมีบริษัทที่สาม (Third party company) หรืออาจจะเป็นแผนกตรวจสอบคุณภาพ (QC) ภายในบริษัท จะทำหน้าที่ตรวจสอบคุณภาพและรับประกันคุณภาพของ software ก่อนที่ software จะถูกนำออกจำหน่าย หน้าที่นี้รวมถึงการตรวจสอบ source code ดังนั้นบริษัทที่สามต้องการยืนยันว่า code ที่ได้มาตรงกับ execute file ที่กำลังตรวจสอบ, ทดสอบ และ เตรียมตัวที่จะจำหน่าย ดังนั้นรหัส MVID จึงเป็นตัวช่วยในระบบการตรวจสอบได้ถูกต้องแม่นยำ

ในหนึ่ง module จะมี table นี้แค่เพียง table เดียวเท่านั้น

จากตัวอย่าง code  ของตอนที่ 2 เราสามารถดูข้อมูลของ ModuleDef ได้ด้วย ILDasm และเลือกที่เมนู View แล้วเลือก MetaInfo แล้วเลือก Show! จะได้ดังรูป




ข้อมูลในกรอบสีแดงคือข้อมูลใน table ModuleDef จริงๆ แล้ว metadata จะเก็บข้อมูลในรูปแบบ table แต่ข้อจำกัดในเรื่องการแสดงผลของ ILDasm จึงไม่สามารถแสดงผลในรูปแบบของตารางได้ ILDasm จึงแสดงข้อมูลในตารางทั้งหมดออกมาดังรูปข้างบน

นอกจากนี้เรายังสามารถดู MVID ผ่าน ทาง command ได้ด้วยคำสั่ง

    c:\>ildasm /all /text Program.exe | find /v "// Time-date stamp:" | find /v "// MVID" | find /v "//  Checksum:" > Program.txt


คำสั่งข้างบนจะเป็นการดึงข้อมูลของ header ใน manage file แล้วบันทึกลงใน text file จากตัวอย่าง text file ของเราคือ Program.txt ดังนั้นเราจึงสามารถ compare ข้อมูลใน header file ด้วยคำสั่ง fc ลองดูตัวอย่างวิธีการใช้ตามรูปข้างล่างน่ะครับ



บทความหน้าพบกับ table ที่ 2 ของ Definition table ครับ ซึ่งจะเป็น table อะไรนั้น .... พบกันใหม่บทความหน้าครับ
TuChay

วันพฤหัสบดีที่ 15 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 4 table ต่างๆ ใน Metadata


execute file ชนิด Managed module จะมี header file ที่มีส่วนสำคัญอยู่ 4 ส่วนดังที่ผมได้เคยอธิบายไปแล้ว หนึ่งใน 4 ส่วนของ header file คือ metadata

Metadata เป็นพื้นที่เล็กๆ ไว้เก็บข้อมูลของ module โดยทำการจัดเก็บในรูปแบบของตาราง (table) โดยสามารถแบ่งออกตารางได้เป็น 3 กลุ่มใหญ่ๆ คือ

Definition tables
table ชนิดนี้จะเก็บข้อมูลของ module นั้นๆ เช่น version, MVID และ Type ชนิดต่างๆ ที่ถูกสร้างขึ้นใน module.

Reference tables
table ชนิดนี้จะเก็บรายละเอียดของ library ที่ module มีการอ้างถึง เช่นรายละเอียดของ System.Console จะถูกเก็บไว้ใน table ส่วนนี้

Manifest tables
table ชนิดนี้แหละครับ เป็นตัวบอกว่า execute file เป็น Assembly Module หรือ Managed module

ในบทความหน้าผมจะอธิบายรายละเอียด tables ที่อยู่ในกลุ่มของ Definition tables ทั้งหมดพร้อมทั้งโชว์ให้ดูว่าเราจะดูข้อมูล tables ของส่วนนี้ได้ที่ไหน

พบกันใหม่บทความหน้าครับ
TuChay

วันพุธที่ 14 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 3 รู้จักกับ Response File


ก่อนที่จะขึ้นรายละเอียดของ Metadata ผมขออธิบายอีกนิดเกี่ยวกับการ save พารามิเตอร์ในการ compile code ลงใน file

โดยปกติตอนที่เรา compile code ด้วยคำสั่ง csc เราอาจจะต้อง reference หลายๆ library ซึ่งจะต้องใส่ /r กับ library ทุกตัว ดังนั้นคำสั่ง csc จะยาวมาก csc หรือ C# Compiler ได้อนุญาติให้เอาพารามิเตอร์ใส่ไว้ลงใน file โดยเรียก file ชนิดนี้ว่า response file ที่มีนามสกุล .rsp เมื่อ คำสั่ง csc compile code และต้องการใช้พารามิเตอร์ใน response file ก็จะใช้เครื่องหมาย @ นำหน้า response file นั้น

ตัวอย่างของ response file




 เมื่อ csc ทำการ conpile code ก็สามารถทำได้ด้วยคำสั่ง

               csc @MyResponse.rsp program.cs

ที่นี้เราจะลดข้อผิดพลาดในการใส่ พารามิเตอร์ของ csc และเรายังใช้พารามิเตอร์ชุดนี้กับ project อื่นๆ ได้อีกด้วย

C# compiler ยังอนุญาติให้มี response มากว่า 1 file ลองดูตัวอย่างครับ

     csc @MyResponse.rsp @AnotherResponse.rsp program.cs

ในบทความที่แล้วผมได้ยกตัวอย่างการ compile code โดยไม่ใส่พารามิเตอร์อะไรเลยเป็นเพราะว่า C# compiler มีการเรียกใช้ response file ที่เป็น global ชื่อ response file ที่เป็น global นี้จะถูกเก็บไว้ในชื่อ csc.rsp โดยถูกเก็บไว้ที่ %SystemRoot%\Microsoft.NET\Framework\vX.X.X

%SystemRoot% คือ directory ที่เก็บ System ของ Windows โดยส่วนใหญ่คือ c:\Windows
vX.X.X คือ version ของ csc โดยปกติจะเป็น version เดียวกับ .Net Framework เราสามารถเช็ค version ของ csc ได้โดยพิมพ์คำสั่ง csc และ enter ดังรูปตัวอย่างข้างล่างครับ



ในรูปข้างล่างเป็นตัวอย่างเนื้อหาของ file csc.rsp


%SystemRoot%\Microsoft.NET\Framework\vX.X.X นอกจากจะเก็บ global response file แล้วยังเก็บ standard library ที่ชื่อ MSCorLib.dll ด้วย

ถ้าเราไม่ต้องการใช้ response file ตัวที่เป็น global เราก็สามารถใส่พารามิเตอร์ /noconfig นอกจากนั้นเรายังใส่พารามิเตอร์ /looger ให้กับ csc เพื่อที่จะ log ข้อมูลของการ compile code ได้ด้วย แต่พารมิเตอร์ /logger ใช้ได้กับ .Net Framework version 4.5 เป็นต้นไป

Response file ที่ผมอธิบายมาทั้งหมดนี้จะใช้ได้เฉพาะ csc compiler เท่านั้นน่ะครับจะไม่สามารถใช้ได้กับ Visual Studio สำหรับ Visual Studio ผมจะอธิบายอย่างละเอียดในบทความถัดๆ ไปครับ

บทความหน้าผมจะเริ่มอธิบายรายละเอียดของ metadata ที่ทำให้ managed module กลายมาเป็น Assembly module น่ะครับ

พบกันใหม่บทความหน้าครับ
TuChay







วันจันทร์ที่ 12 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 2 จาก Type สู่ Assembly Module


เราได้เรียนรู้แล้วว่าการเขียนโปรแกรมด้วยภาษาในตระกูลของ ,Net Framework ซึ่งมีกฏของ CTS ระบุไว้ว่าใน 1 program จะต้องมีอย่างน้อย 1 Type หรือ 1 class การแปลงจาก Type มาเป็น assembly module เป็นยังไง เรามาลองดูตัวอย่าง code ง่ายๆ ตาม code ข้างล่างน่ะครับ

using System;

public class Program
{
    public static void Main()
    {
            System.Console.WriteLine("Hello World");
   
    }
}

code ข้างบนให้เขียนด้วยโปรแกรม notepad แล้ว save เป็น file ชื่อ Program.cs ที่ผมให้เขียนด้วยโปรแกรม notepad เพราะในบทความถัดไปผมจะอธิบายเรื่อง .netmodule ซึ่ง VisualStudio ไม่อนุญาติให้ compile code เป็น .netmodule เฉพาะคำสั่ง csc เท่านั้นที่อนุญาติ

จากนั้นเปิดโปรแกรม Command Prompt ของ Visual Studio .Net ตามรูปน่ะครับ





แล้วก็เริ่ม compile code ด้วยคำสั่ง

          csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs

คำสั่ง csc.exe เป็นคำสั่งของ C# compiler โดยมีรายละเอียดของพารามิเตอร์ดังต่อไปนี้

 /out:Program.exe (/out = output) เพื่อให้ compiler สร้าง execute file ที่ชื่อ Program.exe

/t:exe (/t = target) โดยปกติ C# compiler แบ่งโปรแกรม execute file ที่ใช้บน Windows ออกเป็น 2 ชนิดได้แก่ GUI (graphisl user interface) และ CUI (Console user interface)
พารามิเตอร์ /t:exe จะเป็นการ compiler ให้ comiple code แล้วสร้าง execute file ในรูปแบบ CUI ( Console user interface)
พารามิเตอร์ /t:winexe  จะเป็นการบอก compiler ให้ compile เป็นแบบ GUI (graphical user interface)
พารามิเตอร์ /t:library จะเป็นการบอก compiler ให้ compile เป็นแบบ library

พารามิเตอร์ /t:exe และ พารามิเตอร์ /t:winexe C# compiler จะสร้าง entry point ลงไปใน CLR Healder ด้วย ดังนั้นตัว code จำเป็นต้องมี function Main อยู่เสมอ

/r:MSCorLib.dll (/r = reference) เนื่องจากใน code มีการใช้ Type System.Console ซึ่งเป็น library มาตรฐานอยู่ใน MSCorLib.dll เราจึงต้องมีการ บอกให้ compiler ทำการ reference Type ภายใน library MSCorLib.dll

เนื่องจาก Type ที่เป็นพื้นฐานต่างๆ เช่น int byte double ต่างเป็น Type ที่อยู่ใน library MSCorLib.dll และทุกๆ โปรแกรมส่วนใหญ่จะมีการใช้ที่บ่อยครั้ง ดังนั้น C# compiler ก็จะอัตโนมัติ reference ไปที่ library นี้ โดยที่ ไม่จำเป็นต้องระบุ /r:MSCorLib.dll ก็ได้

defaul target ของ C# Compiler เป็น CUI รวมทั้ง default output file name ก็เป็นชื่อเดียวกับชื่อ file ของ source code (ในตัวอย่างคือ Program.cs) ดังนั้นเราจึงตัด พารามิเตอร์ /t:exe และ พารามิเตอร์ /out:Program.exe ออกไปได้เช่นกัน

ดังนั้น เมื่อต้องการ compile code ก็สามารถเขียนสั้นๆ ได้เป็น

                   csc.exe Program.cs

สำหรับคำสั่ง csc ยังมี พารามิเตอร์อีกหนึ่งตัวคือ /nostdlib สำหรับกรณีที่เราไม่ต้องการใช้ standard library ของ .Net Framework หมายความเราทำ library ของตัวเองขึ้นมาทั้งหมด ดังนั้นการใช้ /nostdlib เมื่อ compile code ไปแล้วเราจะได้ managed module และยังเป็น Assembly Module ในตัวเดียวกัน

แต่จาก code ตัวอย่างจะเห็นว่า Program.cs ไม่สามารถทำงานได้ด้วยตัวเองยังต้องใช้ standard library ของ .Net Framework  ดังนั้นตัวโครงสร้างของ execute file Program.exe ในส่วนของ metadata (metadata เป็นตัวเก็บข้อมูลของ Type ที่ผมได้อธิบายคร่าวๆ แล้วที่ บทความนี้) จึงเป็นตัวทำให้ Program.exe กลายเป็น Assembly Module

metadata ผมจะอธิบายอย่างละเอียดในบทความถัดไปครับ

พบกันใหม่บทความหน้าครับ
TuChay

วันพฤหัสบดีที่ 1 ตุลาคม พ.ศ. 2558

Assembly Module : ตอนที่ 1 บทนำ


บทความที่ผ่านมาผมได้แนะนำให้รู้จักกับ Managed Module กันมาแล้ว ซึ่งใน Managed module จะมีทั้งหมด 4 ส่วนคือ PE, CLR Header, Metadata และ IL language แต่จริงๆแล้วโปรแกรมที่เราเขียนขึ้นจะทำงานได้ไม่ใช่แค่ Managed module เพราะโปรแกรมจะมีการเรียกใช้ library พื้นฐาน ต่างๆของ .Net framework เช่น int หรือ string ซึ่งเป็น Type ที่อยู่ใน namespace System ของ MSCorelib.dll

การทำงานรวมกันระหว่างโปรแกรมที่เราเขียนและ library อื่นๆ ไม่ว่าจะเป็นของ .Net framework หรือ library ของคนอื่นๆ จะทำงานร่วมกันภายใต้ Assembly module ที่ผมกำลังจะอธิบายต่อจากนี้

เนื่องจากผมจะพยายามอธิบายในเรื่องของหลักการ ดังนั้นตัวอย่างการเขียน code จะเน้นไปที่เขียน code ด้วย notepad และการ compile code ก็จะใช้คำสั่ง csc ผ่าน command เพราะว่า Visual Studio มี option ที่ให้นักพัฒนาสามารถ config compiler ได้โดยที่ไม่ต้องรู้รายละเอียดมากนัก จนทำให้เราขาดความรู้ความเข้าใจอย่างลึกซึ้งเกี่ยวกับ .Net framework ผมหวังว่าบทความต่อไปนี้ผู้อ่านจะเข้าใจหลักการพื้นฐาน ของ .Net framework มากขึ้นครับ

พบกันใหม่บทความหน้าครับ
TuChay

วันจันทร์ที่ 28 กันยายน พ.ศ. 2558

type conversion แบบ explicit และ implicit ตอนที่ 3


ในตอนที่ 2 ผมแสดงให้เห็นการ convert Type ถ้าเลือก Type ไม่เหมาะสม ตัวแปรที่เก็บค่าอาจจะมีความผิดพลาดได้ และผมได้ทิ้งคำถามว่าถ้างั้นเราใช้ Type ที่เก็บค่าได้มากๆ เลยดีไหมจะทำให้ไม่มีการผิดพลาดในการเก็บข้อมูล

เพื่อพิสูจน์ความคิดนี้เรามาลองดูตัวอย่าง code นี้น่ะครับ

     const int _count = 1000000;
     long bytes1 = GC.GetTotalMemory(true);
     int[] array = new int[_count];
     array[1] = int.MaxValue;     
     long bytes2 = GC.GetTotalMemory(true);
     long difference = bytes2 - bytes1;
     double per = (double)difference / _count;
     Console.WriteLine("Program used: {0:0.0} MB",
            (double)difference / (1024 * 1024));
     Console.WriteLine("Each decimal element required: {0:0.0}  

             bytes", per);


ในตัวอย่าง โปรแกรมมีการถาม GC (Garbage Collector - ผมจะอธิบายอย่างละเอียดในบทความถัดๆ ไปครับ) ว่า ณ ตอนนี้โปรแกรมมีการใช้หน่วยความจำไปเท่าไร ด้วยคำสั่ง

             long bytes1 = GC.GetTotalMemory(true);

จากนั้นโปรแกรมจะทำการจองหน่วยความจำ โดยทำการสร้าง Array ที่เก็บข้อมูลชนิด integer จำนวน 1,000,000 dataและให้ array ตำแหน่งที่ 1 เก็บค่ามากที่สุดของ Type ชนิด integer.

     int[] array = new int[_count];
     array[1] = int.MaxValue; 
   


จากนั้นโปรแกรม ถาม GC อีกครั้งว่าโปรแกรมมีการใช้หน่วยความจำไปเท่าไร

     long bytes2 = GC.GetTotalMemory(true);

ดังนั้นโปแกรมก็จะรู้ว่าการจองหน่วยความจำ array เพื่อเก็บข้อมูลชนิด integer จำนวน 1,000,000 ใช้หน่วยความจำไปเท่าไรด้วยคำสั่ง

    
     long difference = bytes2 - bytes1;

และจาก code โปรแกรมยังบอกหน่วยความจำที่ใช้แต่ล่ะ array ด้วยคำสั่ง

     double per = (double)difference / _count;

จากนั้นโปรแกรมก็พิพม์ผลออกทางหน้าจอ

     Console.WriteLine("Program used: {0:0.0} MB",
            (double)difference / (1024 * 1024));
     Console.WriteLine("Each decimal element required: {0:0.0}  

             bytes", per);

สำหรับ  Console.WriteLine("Program used: {0:0.0} MB",(double)difference / (1024 * 1024)); สามารถเขียนได้อีกแบบ คือ

     Console.WriteLine("Program used: " + ((double)difference / (1024 * 1024)).ToString("0.0") + " MB");

เนื่องจาก GC.Collector จะ ให้ค่ามีหน่วยเป็น byte
ถ้าหารด้วย 1024 เราก็จะได้หน่วยที่เป็น KB
ถ้าหารด้วย 1024*1024 เราก็จะได้หน่วยที่เป็น MB

ที่นี้เรามาดูผลการรันน่ะครับ



จะเห็นว่าโปรแกรมใช้หน่วยความจำไป 3.8M เราแต่ล่ะ block ของ array จะใช้หน่วยความจำ 4 byte

ที่นี้เราลองมาเปลี่ยน code เป็นดังนี้

            const int _count = 1000000;
     long bytes1 = GC.GetTotalMemory(true);
     decimal[] array = new decimal[_count];
     array[1] = int.MaxValue;
     long bytes2 = GC.GetTotalMemory(true);
     long difference = bytes2 - bytes1;
     double per = (double)difference / _count;
     Console.WriteLine("Program used: {0:0.0} MB",
                (double)difference / (1024 * 1024));
     Console.WriteLine("Each decimal element required: {0:0.0} 

                ytes",per);


ในตัวอย่าง code ที่สองเปลี่ยนแค่ array ที่เก็บ Type ชนิด integer ไปเก็บ Type ที่เป็น decimal

ลองดูผลการรันโปรแกรม



จะเห็นว่ามีการใช้หน่วยความจำไปถึง 15.3 MB ทั้งๆ ที่เก็บข้อมูลเหมือนๆ กับตัวอย่าง code แรก

จากผลการทดลองนี้เราก็สรุปได้ว่า การใช้ตัวแปรที่เก็บข้อมูลได้เยอะโปรแกรมก็จะต้องการหน่วยความจำมากขึ้นดังนั้นเมื่อเราเขียนโปรแกรมเราจึงควรเลือกใช้ Type ให้เหมาะสม โดยเฉพาะตัวแปรของ library ที่พัฒนาเพื่อให้คนอื่นนำไปใช้

พบกันใหม่ในบทความหน้าครับ
TuChay

    



type conversion แบบ explicit และ implicit ตอนที่ 2


จากตอนที่ 1 เราเห็นแล้วว่า การใช้ explicit กับ implicit จะมีรูปแบบการเขียน code เหมือนกัน ในตอนนี้เราจะมาดูข้อแตกต่างกันน่ะครับ

op_implicit
จะใช้สำหรับการแปลง Type โดยที่ไม่สนใจกับการสูญหายของข้อมูลและความแม่นยำในการแปลง ตัวอย่างเช่น Type ชนิด Decimal เมื่อแปลงเป็น Type ชนิด integer ข้อมูลบางส่วนอาจจะหายได้ 

ลองดู code ตัวอย่างน่ะครับ

       double testDouble = (double)Int32.MaxValue + 1;
       int testInt = (int)testDouble;
       Console.WriteLine("double value = " + testDouble.ToString());
       Console.WriteLine("int value = " + testInt.ToString());

เมื่อเราทำการ run software จะได้ดังรูป



จะเห็นว่าถ้าค่าที่แปลงน้อยกว่า 2,147,483,648 การแปลงจาก Type ชนิด double ไปเป็น Integer จะไม่มีปัญหาแต่ถ้าเกินเมื่อไรค่าที่ได้จะให้ตัวเลขที่ผิดพลาดทันที

op_explicit
จะใช้สำหรับการแปลง Type โดยที่สนใจกับการสูญหายของข้อมูลหรือต้องการความแม่นยำในการแปลงเช่น การแปลง Type ชนิด Decimal ไปเป็น Double
ลองดูตัวอย่าง code น่ะครับ

     decimal testDecimal = Decimal.MaxValue;
     double testDouble = (double)testDecimal + 1;
     Console.WriteLine("decimal value = " + testDecimal.ToString());
     Console.WriteLine("double value = " + testDouble.ToString());


เมื่อเราทำการ run software จะได้ดังรูป


จะเห็นว่าเมื่อแปลงข้อมูลมาแล้ว ข้อมูลยังคงถูกต้องเสมอ

จากตัวอย่างจะเห็นว่างั้นเราควรเลิกใช้ int แล้วหันมาใช้ decimal อย่างเดียวกันดีกว่าเพราะการแปลงข้อมูลแม่นยำมาก .... ในบทความหน้าผมจะแสดงให้ดูครับว่าเราควรเลิกใช้ int โดยเปลี่ยนไปใช้ decimal ดีกว่าหรือเปล่า

พบกันใหม่บทความหน้าครับ
TuChay

วันพฤหัสบดีที่ 24 กันยายน พ.ศ. 2558

Type conversion แบบ explicit และ implicit ตอนที่ 1



ผมได้อธิบายเกี่ยวกับ Type และ CLS มาได้หลายบทความแล้ว วันนี้ผมจะอธิบายเกี่ยวกับ การ convert Type ซึ่งเป็นคนล่ะอย่างกับการ convert ตัวแปร ผมเข้าใจว่าผู้อ่านคงรู้จักการ convert ชนิดตัวแปร เช่นการ convert จาก ตัวเลขเป็นตัวหนังสือโดยใช้ method ToString() หรือการ cast ตัวแปรเช่น (int)100

ในชุด .Net Framework มี operation ในการ convert Type อยู่ 2 อย่างคือ op_Implicit กับ op_Explicit วิธีการใช้การ convert Type ของทั้งอย่างจะเหมือนกัน แต่ข้อแตกต่างผมจะไปอธิบายในตอนที่ 2 น่ะครับ ตอนนี้ผมจะอธิบายวิธีใช้ของทั้งคู่ได้ยังไงบ้าง

สมมุติผมมี Type ที่ชื่อ Currency ซึ่งมีรายละเอียดดังนี้

    public class Currency
    {
        public double Rate;
        public string Sign;

        public Currency(double rate, string sign)
        {
            Rate = rate;
            Sign = sign;
        }
        public static implicit operator double(Currency rhs)
        {
            return rhs.Rate;
        }
    }


 จาก code ข้างบนเรามา focus ที่ method นี้ น่ะครับ

        public static implicit operator double(Currency rhs)

ในที่นี้เราจะบอก compiler ว่าเรามี method ชนิด static ที่เป็น operator double โดยมีการรับค่าเข้ามาเป็น Type Currency และมีการ return ค่าที่เป็น Type double  

ตรงนี้สำคัญน่ะครับ Type ที่จะ return ออกไปจะต้องเป็น Type ชนิดเดียวกับ operator ในตัวอย่างนี้ method คือ operator double ดังนั้น Type ที่จะ return จะต้องเป็น Type double ตรงนี้สำคัญมาก

เมื่อมาดูที่ IL code เราจะเห็นว่า C# compiler ได้สร้าง method ที่ชื่อ op_Implicit : float64(class ImplicitTester.Currency)จริงๆ วิธีการตั้งชื่อก็มาจากการนำคำว่า "op_Implicit" มาเป็นชื่อ method เลย โดยที่จะมีการ convert Type ชนิด Currency ไปเป็น float64 



Type ชนิด double ในภาษา C# จะถูกแปลงเป็น Type float64 ใน IL ถ้าอยากใช้ Type double ในภาษา IL จะต้องใช้คำสั่ง [mscorlib]System.Double 

เมื่อเรา double click ที่ method op_Implicit : float64(class ImplicitTester.Currency) เราจะได้ IL code ของ method นี้ดังรูปข้างล่าง



ที่นี้เรามาดูตัวอย่างวิธีการใช้ implicit operator ของ Type Currency กับครับ

            Currency thai = new Currency(35.20, "BTH");
            Console.WriteLine((double)thai);


ที่คำสั่ง (double)thai ก็คือ method public static implicit operator double(Currency rhs) นั้นเอง โดย (double) ก็คือ operator double และ Thai ก็คือ Type ชนิด Currency ตัวอย่างนี้ถ้าพูดเป็นภาษาไทยง่ายๆ ก็คือการแปลง Type ชนิด Currency ไปเป็น Type double

ที่นี้เรามาลองเพิ่ม explicit operator ของ Type Currency ดูน่ะครับ (ตัวอย่างข้างบนเป็น implicit operator น่ะครับ อย่าเพิ่งงง)

        public static explicit operator string(Currency rhs)
        {
            return rhs.Sign;
        }


จะเห็นว่า operator ของ method นี้เป็น Type ชนิด string นั้นหมายความ method นี้จะต้อง return ค่าออกมาเป็น string

เมื่อ C# compiler ทำการ compile code ตัว compiler ก็จะสร้าง method ขึ้นมาใหม่ชื่อ op_Explicit ตามรูปข้างล่าง



method op_Explicit จะทำการ convert Type Currency ออกมาเป็น string

และเมื่อ doublic clik เพื่อเข้าไปดู code ข้างใน method ก็จะได้ดังรูปข้างล่าง




สำหรับตัวอย่างวิธีการใช้ก็คือ

            Currency thai = new Currency(35.20, "BTH");
            Console.WriteLine((string)thai);


ลองมาดูตัวอย่างสุดท้าย โดยการเพิ่ม operator Currency ลงไปใน Type Currency

        public static explicit operator Currency(string sign)
        {
            return new Currency(0, sign);
        }



วิธีการใช้ก็ตามตัวอย่างข้างล่างครับ

       Currency cny = (Currency)("CNY");

พูดง่ายๆ ก็คือ convert Type ที่เป็น string ไปเป็น Type Currency

รูปแบบการเขียน code ระหว่าง explicit และ implicit จะเหมือนกันแต่วิธีการเอาไปใช้จะแตกต่างกันเล็กน้อยซึ่งผมจะอธิบายพร้อมยกตัวอย่างในตอนที่ 2 ครับ

พบกันใหม่บทความหน้าครับ
TuChay

วันอังคารที่ 22 กันยายน พ.ศ. 2558

CLS ตอนกฏเกี่ยวกับ การตั้งชื่อ

 
ที่นี้มาดูกฏข้อถัดไปของ CLS น่ะครับ ในบทความนี้จะเป็นเรื่องของการตั้งชื่อ ภาษาของตระกูล.Net Framework มีทั้งเป็นแบบ case sensitive และ case insensitive คือ ตัวหนังสือตัวใหญ่กับตัวเล็ก จะมีผลกับการ compile code  เช่น ภาษา Visual Bais จะไม่มีผลแต่กับบางภาษาเช่น ภาษา C# ตัวใหญ่กับตัวเล็กจะมองเห็นเป็นคนล่ะตัวกัน

ตัวอย่าง code ข้างล่างเมื่อ compile มาแล้วจะได้ warning message เกี่ยวกับ CLS-compliant


using System;

[assembly:CLSCompliant(true)]

namespace CLS
{
    public class person
    {
    }

    public class Person : person
    {

    }
}


เนื่องจาก code มี 2 Type คือ person กับ Person ถ้าในแง่ของ C# Type ทั้ง 2 ตัวนี้ถือเป็นคนล่ะชนิดกัน แต่ถ้าเรา compile เป็น DLL แล้วไปใช้กับภาษา Visual Basic.Net ตัว Visual Basic จะแยกไม่ออกระหว่าง person กับ Person เพราะ Visual Basic จะมองเห็นเป็น Type ชนิดเดียวกัน

นอกจากนั้นการตั้งชื่อ Type หรือชื่อ class เราไม่สามารถนำ คำสงวน มาตั้งเป็นชื่อได้ ถ้าในภาษา C# compiler จะ compiler จะ return error ทันที ถ้ามีการนำคำสงวนมาใช้ แต่กับภาษา Visual Basic .Net ตัว compiler อนุญาติให้นำคำสงวนมาใช้ได้โดยใช้ "[" กับ "]" ครอบตัวชื่อ Type

ดูตัวอย่าง code ของ Visual Basic น่ะครับ

Public Class [case]
    Private MyName As String

    Public Sub New(ByVal name As String)
        Me.MyName = name
    End Sub

    Public ReadOnly Property ClientName As String
        Get
            Return Me.MyName
        End Get
    End Property

End Class


จะเห็นว่าในตัวอย่าง code มีการนำคำสงวน case มาตั้งเป็นชื่อ Type ถ้าเราเจอแบบนี้ เราสามารถเรียกใช้ได้ใน ภาษา C# โดยใช้ @ นำหน้า Type นั้นๆ ดู ตัวอย่าง code ที่เรียกใช้โดย C# ข้างล่างเลยครับ

            @case ca = new @case("TEST");
            Console.WriteLine(ca.ClientName);


พบกันใหม่บทความหน้าครับ
TuChay


วันพฤหัสบดีที่ 17 กันยายน พ.ศ. 2558

CLS ตอนกฏเกี่ยวกับ Type


จริงๆ บทความนี้เป็นตอนที่ 3 ต่อจาก ตอนที่ 2 แต่ผมดูจากเนื้อหาแล้วน่าจะเยอะและแต่ล่ะตอนก็ไม่ได้ต่อเนื่องกันเลยเปลี่ยนชื่อบทความเป็นชื่อตอนน่ะครับ

ที่นี้เรารู้แล้วว่า CLS compliant  เป็นกฏที่ Microsoft สร้างขึ้นเพื่อที่เป็นข้อกำหนดการใช้ library ร่วมกันระหว่างภาษาในตระกูล .Net Framework ในบทนี้เราจะพูดกันถึงเรื่อง กฏที่เกี่ยวกับ Type

ก่อนอื่นตกลงกันก่อนว่า Type ที่ Microsoft หมายถึงก็คือ ชื่อ class ของ C# นั้นเอง

ในกฏนี้จะบังคับใช้สำหรับ 4 กรณีน่ะครับ [อ่าน description แล้วอาจจะ งงๆ มันจะคล้ายๆ ภาษากฏหมาย ลองดูตัวอย่าง เพื่อเพิ่มความเข้าใจน่ะครับ]

1 กฏจะมีผลสำหรับ Type ที่เป็น public เท่านั้น
ตัวอย่าง code

using System;
using System.Runtime.InteropServices;

[assembly: CLSCompliant(true)]

namespace CLSLib
{

    public class PersonController
    {

    }

    class Person
    {

    }

}

    จากตัวอย่าง code กฏที่เกี่ยวกับ Type จะมีผลเฉพาะ Type PersonController เท่านั้น Type Person จะไม่มีผลกับ CLS-compliant และสมาชิกของ Person ก็จะไม่มีผลกับกฏ CLS-Compliant เช่นกัน

2 กฏจะมีผลสำหรับสมาชิกของ Type ที่เป็น public ของ Type ชนิด public 
 ตัวอย่าง code

    public class PersonController
    {
        private UInt16 _age;

        public Int16 Age
        {
            get { return (Int16)_age; }
        }

        public PersonController()
        {

        }
    }


   
      จากตัวอย่าง code สมาชิก _age ของ PersonController จะไม่มีผลกับกฏ CLS-Compliant แต่สมาชิก Age เป็นชนิด public จึงต้องทำให้ถูกกฏ CLS-Compliant
      ถ้า Age มี Type ชนิด UInt16 เมื่อ compile code เราจะได้ warning message ว่าผิดกฏ CLS-Compliant (ดู ตอนที่ 2 เพื่อความเข้าใจ)

3 กฏจะมีผลสำหรับ return Type และ parameter Type ของ  method ที่เป็น public ของ Type ที่เป็นชนิด public 

   public class PersonController
    {

        public Int16 GetAge(int personID)
        {
            return 0;
        }

        private UInt16 getAge(UInt16 personID)
        {
            return 0;
        }

        public PersonController()
        {

        }
    }


   สำหรับตัวอย่าง code นี้ method getAge จะไม่มีผลกับกฏ CLS-Compliant แต่กับ method GetAge ที่เป็นชนิด public จะมีผลกับ CLS-Compliant.


4 กฏจะมีผลกับการ Type ที่มีการสืบทอด
กฏข้อนี้ก็ง่ายๆ เลยครับ Type ที่จะถูกสืบทอด C# compiler จะบังคับให้ class ชนิดนี้เป็น public อยู่แล้ว เมื่อ class เป็น public ตัว Type ก็จะต้องเข้าตามกฏ CLS-Compliant ข้อที่ 1


Type ที่แสดงข้างล่างจะเป็น Type ที่เป็น CLS-compliant

Byte                  unsigned interger ชนิด 8 บิต มีค่าได้ตั้งแต่ 0 ถึง 255
Int16                 signed integer ชนิด 16 บิต มีค่าได้ตั้งแต่ -32,768 ถึง 32,767
Int32                 signed integer ชนิด 32 บิต มีค่าได้ตั้งแต่ -2,147,483,648 ถึง 2,147,483,647 
Int64                 signed integer ชนิด 64 บิต มีค่าได้ตั้งแต่ -9,223,372,036,854,775,808 ถึง 9,223,372,036,854,775,807 
Single               จริงๆ ก็คือ Type float ในภาษา C# มีค่าได้ตั้งแต่ -3.4 × 1038 ถึง +3.4 × 1038
มีจุดทศนิยมได้ 7 หลัก
Double              จริงๆ ก็คือ Type double ในภาษา C# มีค่าได้ตั้งแต่ ±5.0 × 10−324 ถึง  ±1.7 × 10308 มีจุดทศนิยมได้ 15 หลัก
Boolean            มีขนาด 1 byte เก็บได้แค่ค่า true กับ false
Char                 สำหรับเก็บตัวอักษรชนิด UTF-16
Decimal            เก็บค่าได้เยอะมาก โดยสามารถใช้คำสั่ง desimal.MaxValue เพื่อดูค่ามากที่สุดที่สามารถเก็บได้และ decimal.MinValue เพื่อดูค่าน้อยที่สุดที่สามารถเก็บได้
IntPtr               เก็บตำแหน่งของ pointer ในภาษา C# เราสามารถเล่นกับ pointer ได้เหมือนกันครับ เดียวจะเล่าให้ฟังในบทความหลังๆ ครับ
String                เก็บตัวข้อความเป็นตัวหนังสือ

จะเห็นว่า float ไม่ได้เป็น Type ชนิด CLS-Compliant น่ะครับ ผมจะยกตัวอย่างในบทความหน้าครับ

พบกันใหม่ตอนหน้าครับ
TuChay

วันอังคารที่ 15 กันยายน พ.ศ. 2558

รู้จักกับ CLS ตอนที่ 2


บทความนี้มีการ reference มาจาก CLS Compliance ของ MSDN

เนื่องจาก CLS ของ Microsoft เป็นมาตรฐานสำหรับการพัฒนา software หรือ library เพื่อให้นักพัฒนาท่านอื่นสามารถนำไปใช้งานได้ ระหว่างที่เรากำลังพัฒนา library เราสามารถให้ compiler ตรวจสอบ code ที่เราเขียนขึ้นว่ารอบรับ CLS-compliant หรือไม่ ด้วยการระบุใน assembly ตามตัวอย่าง code ข้างล่าง

using System;

[assembly:CLSCompliant(true)]

namespace CLS
{
   
    public class Person
    {
        public Person()
        {

        }

        public Int32 Age
        {
            get { return 0; }
        }
    }
}



ถ้าเราเขียนโปรแกรมด้วย Visual Studio เราควรนำ [assembly:CLSCompliant(true)] ไปไว้ใน file AssemblyInfo.cs ภายใต้ folder Properties แต่ต้องจำไว้ว่า CLSCompliant เป็น member ของ namespace System จึงต้องมั่นใจว่าที่ file AssemblyInfo.cs มีการ using System; น่ะครับ

สำหรับตัวอย่าง code ข้างบนผมนำเอา [assembly:CLSCompliant(true)] มาไว้ที่ main code

จาก code เมื่อทำการ compile เราจะไม่ได้ warning message แต่ถ้าเปลี่ยน return type ของ property Age จาก Int32 เป็น UInt 16 ดัง code ข้างล่าง

        public UInt16 Age
        {
            get { return 0; }
        }


compiler สามารถ compile code และสร้าง manage file สำเร็จแต่ compiler จะ report warning message "Type of 'CLS-Person.Age' is not CLS-compliant" ตัวอย่างรูปข้างล่างน่ะครับ




สำหรับตัวอย่างนี้สาเหตุที่ไม่เป็น CLS-compliant เป็นเพราะว่าไม่ใช่ทุกภาษาที่จะรองรับ type UInt16 โดยเฉพาะภาษา VB.Net ดังนั้นถ้า class นี้ถูกนำไปใช้กับภาษา VB.Net ตัว property Age อาจจะใช้งานได้ไม่ถูกต้อง

แต่ถ้าเรามั่นใจว่า property Age สามารถใช้ได้กับทุกภาษาและต้องการ by pass การเช็ค CLS-compliant เฉพาะ property Age เราสามารถเขียน code ได้ดังนี้

                    [CLSCompliant(false)]
        public UInt16 Age
        {
            get { return 0; }
        }



แค่นี้เราก็สามารถ compile code โดยที่ไม่มี warning messageแล้วครับ แต่ไม่ควรทำน่ะ

พบกันใหม่ตอนหน้าตอนที่ 3 ครับ
TuChay


วันเสาร์ที่ 12 กันยายน พ.ศ. 2558

รู้จักกับ CLS ตอนที่ 1


บทความนี้มีการ reference มาจาก CLS Compliance ของ MSDN

จนมาถึงตอนนี้เราก็รู้จักกับ .Net Framework และภาษา IL กันมากขึ้น จากบทความ Common Type System ที่ผ่านมาผมได้อธิบายไปแล้วว่าเราสามารถที่จะเขียนภาษาเป็นของตัวเองได้ ในบทความชุดนี้จะเป็นการกล่าวถึงกฏการพัฒนา component software ที่เราต้องปฏิบัติตามอย่างเคร่งครัด

component software คือการพัฒนา software ชนิด library เพื่อให้นักพัฒนา software หรือ programmer ทีมอื่นเป็นคนเรียกใช้ ตัวอย่างของ component software เช่น driver library ของ hardware ที่เราพัฒนา hardware ชุดนั้นขึ้นมาเอง หรือ library สำหรับให้คนอื่นติดต่อกับ software system ด้วย Web Service เป็นต้น

จะเห็นว่า component software ที่เราพัฒนาขึ้น เราไม่สามารถระบุเจาะจงภาษาของนักพัฒนา software ที่เรียกใช้ library ของเราได้ ดังนั้นจึงเป็นไปได้ว่าคนที่เรียกใช้ library ที่เราพัฒนาจะพัฒนาด้วยภาษาที่หลายหลายและด้วยข้อกำหนดหรือรูปแบบของการเขียน code ของแต่ล่ะภาษาก็จะไม่เหมือนกัน เพื่อให้ component software หรือ library ของเราใช้ได้กับทุกๆ ภาษาของตะกูล .Net Framework ดังนั้น Microsoft จึงเขียนข้อกำหนด เกี่ยวกับการพัฒนา library ใส่ลงไปใน ECMA-335 หัวข้อ Stand Common Language Infrastructure โดยข้อกำหนดนี้ถูกกำหนดภายใต้ชื่อ CLS - Common Language Specification.

ในบทความชุดนี้จะเป็นการพูดถีงข้อปฏิบัติตามกฏของ CLS ซึ่งจะเป็นอะไรบ้างนั้น .... พบกันใหม่บทความหน้าครับ
TuChay

วันพฤหัสบดีที่ 10 กันยายน พ.ศ. 2558

Indexer ใน C# ตอนที่ 2



เรามาต่อจากตอนที่ 1 กันเลย

ในตอนที่แล้วผมได้เขียน code ที่ indexer มี signuature เป็น int อย่างเดียว ที่นี้เราลองเพิ่ม Indexer อีกหนึ่งตัวน่ะครับที่มี signature เป็น string.

        public int this[string name]
        {
            get
            {
                for (int i = 0; i < 7; i++)
                {
                    if (nameOfDay[i] == name)
                        return i;
                }
                return 99;
            }
        }


ใน IL code เราก็จะได้ method ที่ชื่อ get_Item อีกหนึ่งตัวแต่ signature ตัวใหม่จะเป็น get_Item : int32(string) คือรับ parameter เป็น string และ return int กลับไปให้ caller.



นอกจากนั้นเรายังจะได้ property อีกหนึ่งตัวที่ชื่อ Item : instance int32(string)

ต่อไปเราลองมาเพิ่ม C# code จาก read only มาเป็น property ที่สามารถ update ได้ ตาม code ข้างล่างครับ

        public string this[int index]
        {
            get
            {
                if (index > nameOfDay.Length)
                    return "";
                else
                    return nameOfDay[index];
            }
            set
            {
                if (index > nameOfDay.Length)
                    throw new Exception("The day should have only 7 day");
                else
                    nameOfDay[index] = value;
            }
        }

จาก นั้นลอง compile code แล้วมาดู IL code กันครับจะเห็น method ที่เพิ่มเข้ามาอีก 1 method คือ set_Item : void(int 32, string)  จะเห็นว่า signature จะเปลี่ยนมาเป็นรับค่า 2 parameter คือ int กับ string


โดยที่ parameter ชนิด int คือ ตำแน่งของ index และ string ก็คือ value ที่ assign ให้กับ index ตัวนั้น


จบแล้วครับสำหรับ Indexer ในภาษา C# และภาษา IL พบกันใหม่บทความหน้าครับ
TuChay


วันพุธที่ 9 กันยายน พ.ศ. 2558

Indexer ใน C# ตอนที่ 1


จาก code ของบทความ Common Type System ตอนที่ 2 ผมได้ยกตัวอย่าง method ชนิด indexer แต่ยังไม่ได้อธิบายอย่างละเอียด ผมขออธิบายรายละเอียดในบทความนี้น่ะครับ

Indexer คือ การดึงข้อมูลหรือ update ข้อมูลผ่าน instance object โดยใช้ index เป็น paremeter เรามาดูตัวอย่าง code กันครับ

  class Program
    {
        static void Main(string[] args)
        {
            Weekly week = new Weekly();
            for(int i=0;i<7;i++)
                Console.WriteLine(week[i]);

            Console.ReadLine();
        }
    }

    public class Weekly
    {
        private string[] nameOfDay = new string[7] {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};


        public Weekly()
        {

        }

        public string this[int index]
        {
            get
            {
                if (index > nameOfDay.Length)
                    return "";
                else
                    return nameOfDay[index];
            }
        }

    }


Indexer ของ class Weekly คือ public string this[int index] เราสามารถเรียกใช้ indexer ผ่าน object ได้เลยด้วย week[i] โดยที่ i ถือเป็น parameter ของ indexer นี้ครับ

เมื่อเรามาดู IL code จะได้ดังรูปข้างล่าง








C# compiler ได้สร้าง method ที่ชื่อ get_Item : string(int32)
string(int32) จะหมายถึง method นี้จะรับ parameter เป็น int32 และ method จะ return string กลับไปให้ caller
code ของ method สามารถเข้าไปดูได้โดยการ dobule click ที่บรรทัดนี้ ชื่อของ method C# compiler ตั้งชื่อตามหลักของ method คือนำ "get_" นำหน้า "Item" เพราะว่าเป็น method ชนิด indexer

เมื่อ เทียบกับภาษา C# method get_Item ก็คือ code นี้ครับ

        public string this[int index]
        {
            get
            {
                if (index > nameOfDay.Length)
                    return "";
                else
                    return nameOfDay[index];
            }
        }


นอกจากนั้น C# Compiler ยังได้สร้าง property ที่ชื่อ Item ใน บรรทัด Item : instance string(int32) เพื่อเป็นการสร้าง indexer สำหรับให้ caller เรียกใช้ เมื่อ double click ที่บรรทัดนี้ จะได้ดังรูป



เนื่องจาก Indexer Weekly ใน C# code ข้างบน เป็นการอ่านแบบ read only คือ ไม่มี คำสั่ง set มีแต่คำสั่ง get ใน IL code ของ property Item จึงมีแค่ .get เท่านั้น

พบกันใหม่ตอนที่ 2 ตอนหน้าครับ
TuChay

วันจันทร์ที่ 7 กันยายน พ.ศ. 2558

รู้จักกับ Shell บน Linux ตอนที่ 2


เรามาต่อจากตอนที่ 1 เลยน่ะครับ

ในช่วงนั้นระบบปฏิบัติการ Unix เป็น Open source แต่เรื่องของ license ยังเป็นสิ่งที่คลุมครือ และมีบางบริษัทนำระบบปฏิบัติการ Unix ที่เป็น Open  source มาพัฒนาต่อเพื่อให้เป็น commercial version ของตัวเองและทำการออกจำหน่าย โดย commercial version ที่จำหน่ายก็ไม่เป็น open source อีกต่อไปซึ่งผิดวัตุประสงค์ของ Open source จึงทำให้เกิด project ที่ชื่อว่า GNU (Genernal public license) โดยผู้ก่อตั้งที่ชื่อ Richard Stallman

หนึ่งใน GNU project คือ Unix-compatible operating system ภายใต้ open sourcee และ free license จุดประสงค์หลักก็เพื่อเอามาแทนที่ Unix ที่มี license เป็นเชิงพาณิชย์ ใน project นี้ มีผู้ดูแลรับผิดชอบเกี่ยวกับ shell คือ Chet Ramey โดย shell ที่เขานำมาใช้ในระบบปฏิบัติการมีชื่อว่า Bourne Again Shell หรือ bash

bash version แรกถูกเขียนขึ้นโดย Brain Fox และถูก release ออกมาเมื่อวันที่ 10 มกราคม 1988 โดยการรวมเอาข้อดีหลักๆ ของ C shell และ Korn shell มาไว้รวมกันโดยพัฒนามาจาก code พื้นฐานของ Bourne shell และในปี 1989 Chet Ramey ได้ดึง Brain Fox เข้ามารวมทีมเพื่อที่จะนำ bash เป็น shell พื้นฐานของ Unix open source ที่เขาดูแลอยู่และในปัจจุปัน Chet Ramery เป็นผู้รับผิดชอบอย่างเป็นทางการในเรื่องของการแก้ bug และ improve feature ต่างๆ ของ bash เนื่องจาก bash เป็น shell แบบ free license จึงเป็นที่นิยมนำมาใช้เป็น shell มาตรฐานใน Linux หลายสายพันธุ์ รวมถึง Mac OS X ของ apple ด้วย

ในวันที่ 23 ธันวาคม 1996 Chet Ramey ได้ release bash version 2.0 (version ก่อนหน้านี้เป็น version 1.14.7)  และ release version 3.0 ในวันที่ 27 กรกฏาคม 2004 จนกระทั่ง version ปัจจุปัน version 4 ที่ Chet Ramey ได้ release เมื่อวันที่ 20 กุมภาพันธ์ 2009

บทความต่อจากนี้ของผมจะพูดเกี่ยวกับการเขียน shell script บน bash เท่านั้นน่ะครับ

แล้วพบกันบทความหน้าครับ
TuChay

วันพฤหัสบดีที่ 3 กันยายน พ.ศ. 2558

รู้จักกับ Shell บน Linux ตอนที่ 1


ในยุคประมาณปี 1960 เป็นยุคเริ่มต้นของระบบ Unix และเป็นที่นิยมอย่างแพร่หลายในปี 1970 ในตอนนั้นเราแบ่งการทำงานของระบบ Unix เป็น 3 ส่วน คือ ส่วนที่เป็น input ส่วนที่ประมวลผล และส่วนที่เป็น output

ส่วนที่เป็น input กับส่วนที่เป็น output เป็นส่วนตรงกลางระหว่าง user หรือผู้ใช้กับหน่วยประมวลผล โดย input จะทำหน้าที่รับคำสั่งผ่านทาง keyboard แล้วแปลงคำสั่งนั้นส่งไปให้ ระบบปฏิบัติการ Unix เพื่อให้ CPU ประมวลผลและเมื่อได้ผลลัพท์ที่ได้จึงนำมาแสดงในส่วนที่ เป็น output นั้นคือ monitor นั้นเอง

ส่วนที่รับคำสั่งผ่านทาง keyboard แล้วแปลงคำสั่งเพื่อส่งไปให้ Unix ประมวลผลแล้วแสดงผลที่ได้ออกทาง monitor นี้แหละครับ เราเรียกว่า shell

ในสมัยนั้น Shell ที่ได้รับความนิยมมากที่สุดคือ Bourne shell version 7.0 สร้างและพัฒนาโดย Steven Bourne โดยเริ่มใช้ตั้งแต่ปี 1979 Bourne shell เราจะเขียนสั้นๆ ว่า sh

เนื่องจากโครงสร้างหรือรูปแบบคำสั่งของ sh เป็นคำสั่งง่ายและยังไม่สนับสนุนคำสั่งที่มีความซับซ้อน  Bill Joy แห่งมหาวิทยาลัย Berkeley จึงได้พัฒนา Shell ใหม่ขึ้นเรียก C shell มีชื่อย่อว่า csh โดยรูปแบบคำสั่งของ shell จะมีลักษณะคล้ายกับภาษา C ทำให้นักพัฒนาที่มีความรู้ภาษา C สามารถสร้างสรรค์คำสั่งที่ซับซ้อนได้ดีขึ้นแต่ข้อเสียของ C Shell คือไม่ backward complitable กับ Bourne shell นั้นคือคำสั่งบางคำสั่งที่เคยใช้ได้ใน Bourne shell กับใช้ไม่ได้ใน C Shell.

หลังจากนั้นก็มี Shell เกิดขึ้นมาใหม่มากมายและเป็นที่นิยมใช้เช่น Korn Shell หรือตัวย่อ ksh โดย David Korn จาก AT&T โดยการรวมข้อดีของ C Shell และ Bourne shell เข้าด้วยกัน Korn shell สามารถ download ได้ฟรีแต่ผู้ download จะต้องจ่ายค่า license สำหรับบางสถาณการณ์

พบกันใหม่ตอนหน้าครับ
TuChay