មេរៀនទី៨: Value និង Regerence

មេរៀនទី៨: Value និង Regerence

ក្នុងជំពូកនេះអ្នករៀនពីរបៀប:

-Copy តំលៃ Value អថេរ

-Copy reference ប្រភេទអថេរ

-ប្រើពាក្យគន្លឹះ ref និង out ចូលទៅក្នុង argument នៃ method parameter។

-Box តំលៃ value ចាប់ផ្តើមឬដាក់អោយអថេរនៃប្រភេទ object ទៅ តំលៃ value ។

-Unbox តំលៃ value ដោយបោះចោល object reference ដែល សំដៅទៅក្នុង boxed value ។

1. Copy អថេរ int និង Class ប្រភេទ primitive ដូចជា int ត្រូវបានហៅថាជាប្រភេទតំលៃ ។ ពីព្រោះ ថាអថេរ int នេះគឺជាតំលៃចំនួនគត់ integer (ដូចជា 42) ។  យ៉ាងណាក៏ដោយ ប្រភេទ class ដូចជា Circle (ដែលបានសរសេរក្នុងជំពូកទី៧) មិនមានប្រភេទតំលៃ; ប្រភេទ reference របស់វា ។ អថេរ Circle មិនមាន ទុក “Circle value”; អ្វីដែលវាទុកគឺជា reference ទៅក្នុង Circle object ។ ម្យ៉ាង ទៀតប្រភេទតំលៃទុកតំលៃនិងប្រភេទ reference ទុកក្នុង reference។ ពីព្រោះមានមធ្យោយខុសគ្នាដាក់ទិន្នន័យ data ប្រភេទតំលៃគឺជួនកាល ហៅផ្ទាល់ direct type និង reference type ជួលពេលខ្លះហៅ indirect types ។ អ្នកត្រូវការដើម្បីស្វែងយល់ស៊ីជំរៅរវាងភាពខុសគ្នានៃប្រភេទតំលៃ value types និង reference type។ ការប្រត្តិបត្តិនៃការប្រកាស i ដូចជា int ដែល i ក្លាយជាឈ្មោះនៃអថេរ មួយដែលដាក់ក្នុងតំលែចំនួនគត់ integer ។ ប្រសិនបើអ្នកប្រកាស declare copy i ដូចជា int ផ្សេតទៀត វាក្លាយជាឈ្មោះនៃអថេរមួយដែលដាក់តំលៃ ចំនួនគត់ ។ ប្រសិនបើអ្នកជ្រើសរើស initialize ឬ assign copy i  ទៅ I និង copy i  និងដាក់តំលៃដូចគ្នា I ។  យ៉ាងណាក៏ដោយ តាមពិតការ copy i និង i បណ្តាលឡើងពីការដាក់តំលៃដូចគ្នា មិនធ្វើអោយមានការ Copy ពីរដង there នៃតំលៃ 42: មួយនៅខាងក្នុង i និងមួយទៀត copy I ។ Code ខាងក្រោមនេះ បង្ហាញច្បាស់លាស់ ។

int i = 42;// declare and initialize i

int copyi = i;// copyi contains a copy of the data in i

i++;// incrementing i has no effect on copy i;

ការប្រត្តិបត្តិនៃការប្រកាស c ដូចជា Circle (ឈ្មោះនៃ class មួយ) គឺមានភាពខុសគ្នាច្រើន ។ កាលណាអ្នកប្រកាស declare c ដូចជា Circle, c ក្លាយជាឈ្មោះនៃអថេរមួយដែលសំដៅលើ Circle object ។ ប្រសិនបើអ្នក

ប្រកាស declare refc ដូច Circle ផ្សេងទៀត វាក្លាយជាឈ្មោះនៃអថេរដែល សំដៅទៅលើ Circle object មួយ ។ ប្រសិនបើអ្នកជ្រើសរើស initialize

ឬ assign refc ទៅ c នោះ refc នឹងសំដៅលើ Circle object ដូចគ្នាដែល c ធ្វើ; មាន Circle object តែមួយគត់ និង refc និង c ទាំងពីរសំដៅលើវា ។ ខាងក្រោមនេះជា code ឧទាហរណ៍:

Circle c = new Circle(42);

Circle refc = c;

មើលក្នុង diagram ទាំងពីរឧទាហរណ៍ខាងក្រោមនេះ:
a

ប្រើតំលៃ value parameter និង reference parameter

1. ចាប់ផ្តើមចូលក្នុង Start Microsoft Visual Studio .NET

2. បើក Parameter project ដែលមានទីតាំងក្នុង\Microsoft Press\Visual C# Step by Step\Chapter 8\Parameters folder ក្នុថ My Documents folder របស់អ្នក Parameters project បានបើកឡើង ។

3. បើក Pass.cs ប្រភព file ក្នុង Code pane នៃ Code និងសន្លឹក Text Editor រួចក្នុងទីតាំង Pass class ។ បន្ថែម public static method ដែលហៅថាតំលៃ Pass class ។ វិធី method នេះនឹង អនុញ្ញាតអោយមាន int parameter ទោល (ប្រភេទតំលៃ) ដែលហៅថា param ហើយនឹងត្រឡប់ទទេ ។  តំលៃនឹងផ្តល់អោយគឺ 42 ទៅ param ។ Pass class នឹងមើលឃើញច្បាស់លាស់ដូចខាង

ក្រោមនេះ:

namespace Parameters

{

class Pass

{

public static void Value(int param)

{

param = 42;

}

}

}

4. បើកប្រភព file Main.cs នៅក្នុង Code pane រួចមើលទីតាំង Entrance method នៃ Application class ។  Main method គឺជា កន្លែងដាក់កម្មវិធីចាប់ផ្តើមដំណើរការ Main method ផ្ទុក statement តែមួយគត់ : ដើម្បីហៅ Entrance ដែលត្រូវបានបញ្ចូលទៅក្នុងបណ្តុំ try បន្ទាប់មកចាប់ catch handler ។

5. បន្ថែម 4 statements ទៅក្នុង Entrance method ។ មុនដំបូង នឹងប្រកាស Declare តំបន់អថេរ int ដែលហៅថា i  និង initialize វា ទៅជា 0 ។  statement ទីពីរនឹងសរសេរតំលៃនៃ i ដើម្បីconsole ។ statement ទីបីនឹងហៅតំលៃ Pass.Value ផ្ញើទៅ i ដូចជា argument និង statement ទីបួននឹងសរសេរម្តងទៀតតំលៃនៃ i ទៅ console ។ ហៅទៅ Console.WriteLine មុននិងបន្ទាប់ហៅទៅ Pass.Value អនុញ្ញាតអោយអ្នកមើលឃើញ Pass.Value ប្តូរតំលៃនៃ I  ntrance method នឹងបង្ហាញដូចខាងក្រោមនេះ:

static void Entrance()

{

int i = 0;

Console.WriteLine(i);

Pass.Value(i);

Console.WriteLine(i);

}

6. នៅលើ Debug menu ចុចលើ Start Without Debugging ។

shortcut key សំរាប់ចាប់ផ្តើមចូលក្នុង Without Debugging គឺចុចលើ Ctrl+F5 ដើម្បី builds និងរត់កម្មវិធី ។

7. បញ្ជក់តំលៃ 0 ត្រូវបានសរសេរទៅសន្លឹក console ពីរដង ។

8. បើកប្រភពដើម file WrappedInt.cs នៅក្នុង Code pane រួចសរសេរក្នុងទីតាំង WrappedInt class ។ បន្ថែម public instance field ដែលហៅថាចំនួននៃប្រភេទ int  ទៅក្នុង WrappedInt class ។

WrappedInt class នឹងមើលឃើញដូចខាងក្រោមនេះ:

namespace Parameters

{

class WrappedInt

{

public int Number;

}

}

9. បើកប្រភពដើម file Pass.cs នៅក្នុង Code pane រួចហើយក្នុង ទីតាំង Pass class ។ បន្ថែម public static method ដែលហៅថា Reference ទៅក្នុង Pass class ។ វិធីនេះនឹងអនុញ្ញាតអោយបាន WrappedInt parameter ទោល ដែលហៅថា param និងប្រភេទ return type ទទេ ។ Reference នឹងផ្តល់ 42 ទៅ param.Number Pass class នឹងបង្ហាញដូច ខាងក្រោមនេះ:

namespace Parameters

{

class Pass

{

public static void Value(int param)

{

param = 42;

}

public static void Reference(WrappedInt param)

{

param.Number = 42;

}

}

}

10. បើកក្នុង file ប្រភពដើម Main.cs ក្នុង Code pane រួចនៅទីតាំង Entrance method នៃ Application class ។

11. បន្ថែម បួន statements ទៅ Entrance method ។ ដែល

statement នឹងប្រកាសតំបន់នៃអថេរ WrappedInt ដែលហៅថា wi និង initialize វាដើម្បីបាន WrappedInt object ថ្មីដែលហៅថា  default constructor ។  statement ទីពីរនឹងសរសេរតំលៃនៃ wi ចំនួនទៅក្នុង console ។ statement ទីបី នឹងហៅ Pass.Reference method ដែលផ្ញើ wi ដូចជា argument មួយ ។ និង statement ទីបួននឹងសរសេរម្តងទៀតតំលៃ wi.Number ទៅក្នុង console ។

Entrance method នឹងបង្ហាញដូចខាងក្រោមនេះ:

static void Entrance()

{

int i = 0;

Console.WriteLine(i);

Pass.Value(i);

Console.WriteLine(i);

WrappedInt wi = new WrappedInt();

Console.WriteLine(wi.Number);

Pass.Reference(wi);

Console.WriteLine(wi.Number);

}

12. នៅលើ Debug menu ចុចលើ Start Without Debugging ។ កម្មវិធី builds និងរត់ដំណើរការ ។ ដូចមុនពេលតំលៃពីរដំបូងដែលបាន

សរសេរទៅក្នុងសន្លឹក console គឺ 0 ហើយ 0 មុននឹងបន្ទាប់ហៅទៅ Pass.Value ។

13. សំរាប់តំលៃទីពីរបន្ទាប់ ដែលទាក់ទងគ្នាទៅនឹងតំលៃ wi មុននឹង

បន្ទាប់ Pass.Reference បញ្ជក់ថាតំលៃនៃ 0 ហើយនឹងតំលៃនៃ 42 ត្រូវបានសរសេរទៅក្នុងសន្លឹក console ។ តំលៃនៃ wi.Number គឺជា initialized to 0 ដោយ default constructor ។

2. ប្រើ ref និង out Parameter

ក្នុងលំហាត់ពីមុន អ្នកបានឃើញកាលណាអ្នកបានផ្ញើ argument មួយទៅអោយ method មួយដែលមាន parameterត្រូវបានចាប់ផ្តើមដូចជា copy នៃ argument ។  ការរៀបចំនេះមានន័យថាវាមិនអាចធ្វើទៅបានដើម្បី ប្តូរ parameter មានឥទ្ធិពលលើ argument ឧទាហរណ៍:

static void DoWork(int param)

{

param++;

}

static void Main()

{

int arg = 42;

DoWork(arg);

Console.WriteLine(arg); // writes 42 not 43

}

បង្កើត ref Parameter

ប្រសិនបើ parameter នៅខាងមុនជាមួយពាក្យគន្លឹះ ref ដែល parameter ក្លាយជាឈ្មោះប្តូរសំរាប់ argument នៅពេល copy argument. ឈ្មោះប្តូរប្រព័ន្ធនេះមានន័យថាបញ្ហាដែលអ្នកធ្វើក្នុង parameter អ្នកធ្វើដោយ automatically ទៅក្នុង argument ពីព្រោះ parameter គឺជាឈ្មោះប្តូររបស់ argument ។ កាលណាអ្នកបានផ្ញើ argument មួយទៅក្នុង ref parameter អ្នកត្រូវតែនៅពីរមុន argument ជាមួយពាក្សគន្លឹះ ref

static void DoWork(ref int param)//using ref

{

param++;

}

static void Main()

{

int arg = 42;

DoWork(ref arg);//using ref

Console.WriteLine(arg);//writes 43

}

ឧទាហរណ៍ ក្នុងឧទាហរណ៍បន្ទាប់ arg គឺមិនត្រូវបានចាប់ផ្តើម initialized ហើយឧទាហរណ៍មិនធ្វើ compile ។  ការ​មិនបានជោគជ័យ failure គឺព្រោះមកពី param++ នៅក្រៅតំលៃពិត arg++​ហើយ arg++ គឺ អនុញ្ញាត arg តែមួយគត់មានន័យច្បាស់លាស់:

static void Value(ref int param)

{

param++;

}

static void Main()

{

int arg; // not initialized

Value(ref arg);

Console.WriteLine(arg);

}

បង្កើត out Parameters

ពាក្សគន្លឹះ out គឹប្រហាក់ប្រហែលនិង ref ។ អ្នកអាចប្រើ parameter នៅខាងមុនជាមួយពាក្សគន្លឹះ out  នេះមានន័យថា parameter ក្លាយជាឈ្មោះកែប្រែបានសំរាប់ argument ។ អ្នកអាចធ្វើវាដោយ automatically ទៅក្នុង argument ព្រោះថា parameter គឺជាឈ្មោះប្តូរកែប្រែសំរាប់ argument ។ កាលណាអ្នកបានផ្ញើ argument មួយទៅ out parameter អ្នកត្រូវតែមាន  argument នៅខាងមុខជាមួយពាក្សគន្លឹះ out ។ ភាពខុសគ្នារវាង ref parameter និង out parameter គឺជាវិធី method មិនអាចធ្វើដើម្បីផ្តល់តំលៃទៅ ref parameter ប៉ុន្តែវាផ្តល់តំលៃទៅ out parameter ។ ម្យ៉ាងវិញទៀត ឧទាហរណ៍ខាងក្រោមនេះមិនអាច compile បានព្រោះមិនអាចផ្តល់តំលៃ param:

static void DoWork(out int param)

{

// Do nothing

}

យ៉ាងណាក៏ដោយ ឧទាហរណ៍ខាងក្រោមនេះធ្វើ compile ពីព្រោះតំលៃ ទៅតំលៃ param:

static void DoWork(out int param)

{

param = 42;

}

ព្រោះថា out parameter ត្រូវតែផ្តល់តំលៃ អ្នកអនុញ្ញាតដាក់នៅខាង ក្រោយវាទៅក្នុង argument ដែលមិនបានកំណត់ច្បាស់លាស់ផ្តល់អោយ ។ កាលណា method បានបញ្ចប់ argument ត្រិះរិះកំណត់ច្បាស់លាស់ និងតំលៃ

របស់វាដើម្បីអាន ឧទាហរណ៍:

static void DoWork(out int param)

{

param = 42;

}

static void Main()

{

int arg; // not initialized

DoWork(out arg);

Console.WriteLine(arg); // writes 42

}

ប្រើ ref parameter

1. បើកក្នុង Parameters project ដែលមានទីតាំងក្នុង \Microsoft Press\Visual C# Step by Step\Chapter 8\Parameters folder ក្នុង My Documents folder របស់អ្នក ។

2. បើក file ប្រភពដើម Pass.cs ក្នុង Code pane រួចមើលក្នុងទីតាំង តំលៃ method ក្នុង Pass class ។

3. ក្នុង Edit តំលៃ method អនុញ្ញាតទៅក្នុង int argument ដូចជា ref parameter ។ តំលៃ នឹងបង្ហាញដូចខាងក្រោមនេះ:

class Pass

{

public static void Value(ref int param)

{

param = 42;

}

}

4. បើកក្នុងប្រភព file ដើម Main.cs ក្នុង Code pane មើលក្នុងទីតាំង Entrance method នៃ Application class ។ ក្នុង Edit មាន statement ទីបីនៃ method នេះដែលមាន Pass.Value method ហៅផ្ញើ argument របស់វាដោយ ref ។ Entrance metho នឹងបង្ហាញ

ដូចខាងក្រោមនេះlook exactly like this:

class Application

{

static void Entrance()

{

int i = 0;

Console.WriteLine(i);

Pass.Value(ref i);

Console.WriteLine(i);

}

}

4. នៅលើ Debug menu ចុចលើ Start Without Debugging ។ កម្មវិធី builds និងរត់ដំណើរការ ។

3. ប្រើ Stack និង Heap

ឥឡូវអនុញ្ញាតចូលឆ្លងកាត់ អ្វីដែលកើតឡើងបន្ទាប់ពី Method ត្រូវ

បានហៅ:

void Method(int param)

{

Circle c;

c = new Circle(param);

}

ឧបមាថាតំលៃដែលផ្ញើទៅអោយ param គឺជាតំលៃ 42 ។  ចំពោះ memory តូច (គឺមិនគ្រាប់គ្រាន់សំរាប់ប្រើ int) ដែលធ្វើអោយបាត់បង់ stack។ នេះគឺ param ដែលជាអត្ថិភាព ។ តំលៃនៅខាងក្នុងតូចបំផុត stack memory ត្រូវ

បានចាប់ផ្តើម ក្នុង 1 bit បង្ហាញក្នុងតំលៃ int 42 ។

Memory តូច (មិនគ្រប់គ្រាន់សំរាប់ reference មួយ) ដែលទទួលពីក្នុង stack ។ តំលៃនៅខាងក្នុង stack memory តូចត្រូវបានចាប់ផ្តើមទៅតំលៃ ដែលយោងទៅតាមលក្ខណ:ពិសេសរបស់ heap memory កន្លែងដែល Circle មានរួចស្រេច ។ Circle constructor នឹងឆ្លងកាត់ exception មួយ ។ ប្រសិន បើវាធ្វើ memory មាននៅក្នុង Circle object នឹងទាញយកតំលៃត្រឡប់មកវិញ ដោយ constructor នឹងមាន null reference ។ ពេលអនុគមន៍ទាំងអស់បញ្ចប់ ។  parameters និងតំបន់អថេរចេញក្រៅនៃ scope ។ memory ទទួល សំរាប់ c ដោយ automatically ចុងបញ្ចប់ទៅដល់ stack ។

4. អ្វីគឺជា System.Object Class

មួយក្នុងចំណោមច្រើនដែលមានសារ:សំខាន់បំផុតគឺ Object class ក្នុងប្រព័ន្ធ System namespace របស់ Microsoft.NET Framework:

វាដូចជាមានសារះសំខាន់ class ដែលពាក្យគន្លឹះ object  គឺតាមពិត ជាឈ្មោះដែលអាចប្តូរបានក្នុង System.Object ។ ក្នុង code របស់អ្នក អ្នកអាចសរសេរ object ឬអ្នកអាចសរសេរ System.Object; វាមានន័យច្បាស់

លាស់ ។ គ្រប់ classes ទាំងអស់មានភាពមិនច្បាស់លាសដែលទទួលពី System.Object class ។ អ្នកមិនត្រូវការដើម្បី declare ក្នុង class របស់អ្នក

ដែលបានមកពី System.Object (ទោះបីអ្នកអាចប្រសិនបើអ្នកចង់ធ្វើ) ។ តាម ពិត class របស់អ្នកគឺជា class ដែលមានន័យវាបានផ្តល់ដោយ automatically ពី System.Object ។  ម្យ៉ាងទៀតប្រសិនបើអ្នកសរសេរដូច

នេះគឺ :

class Circle

{

}

តាមពិតអ្នកសរសេរដូចនេះ :

class Circle : System.Object

{

}

ឧទាហរណ៍ខាងក្រោមនេះ អថេរ c និង o ទាំងពីរនេះយោងតាម Circle object ។ តាមពិតប្រភេទនៃ c គឺជា Circle និងប្រភេទ o គឺជា object (ឈ្មោះអាចប្តូរ System.Object) ក្នុងការអនុវត្តមានពីរប្រភេទខុសគ្នា “views” នៃ object ដូចគ្នា:

Circle c;

c = new Circle(42);

object o; o = c;
a

5. Boxing

ដូចដែលអ្នកបានឃើញ អថេរប្រភេទ object យោងតាម object នៃ ប្រភេទ reference នីមួយៗដែលវាអាចទៅដល់ instance នៃ class មួយ ។ ទោះបីជាអថេរប្រភេទ object អាចយោងទៅតាមតំលៃនៃប្រភេទនីមួយៗ ។ ឧទាហរណ៍ ខាងក្រោមនេះមានពីរ statements initialize អថេរ i (ប្រភេទ int តំលៃប្រភេទ) 42 និង initialize អថេរ o (ប្រភេទ object និងប្រភេទ reference type) ទៅ i:

int i = 42;

object o = i;

អ្នកគួរចងចាំថា i គឺជាប្រភេទតំលៃហើយវាមានរួចស្រេចក្នុង stack ។ ប្រសិនបើ reference នៅក្នុង o ទៅ I នោះ reference នឹងទៅដល់ stack ។ ជំនួសអោយតំលៃ copy នៃតំលៃនៅខាងក្នុងនៃតំលៃ i ត្រូវបានបង្កើត heap និង reference នៅក្នុង o យោងទៅតាម copy ។
a

6. Unboxing

ព្រោះអថេរនៃប្រភេទ object យោងទៅតាម boxed copy នៃតំលៃ វាសមហេតុផលអនុញ្ញាតអោយអ្នកទទួលតំលៃ boxed ឆ្លងកាត់អថេរ ។ អ្នក

អាចជឿជាក់ថាមានលទ្ធភាពដើម្បីអនុញ្ញាតទៅក្នុង boxed តំលៃ int ដែលអថេរ o ដោយប្រើ int i = o; ។ ទោះបីយ៉ាងណាក័ដោយ ប្រសិនបើអ្នកព្យាយាម syntax នេះអ្នកនឹងទទួលបាន compile time error ដូចឧទាហរណ៍ខាង

ក្រោមនេះ example:

Circle c = new Circle();

int i = 42;

object o;

o = c; // o refers to a circle

i = o; // what is stored in i?

អ្នកប្រើអ្វីដែលដឹងដូចជា cast សំរាប់អនុវត្តន៍ទៅប្រភេទផ្សេងទៀត ។ អ្នកប្រើអថេរ object ដាក់នៅពីមុខឈ្មោះនៃប្រភេទរវាងសញ្ញា //  ដូចបង្ហាញ

ក្នុងឧទាហរណ៍  :

int i = 42;

object o = i; // boxes

i = (int)o; // compiles okay

 

ទោះជាយ៉ាងណាក៏ដោយ ប្រសិនបើ o មិនធ្វើក្នុង boxed int មាន ប្រភេទមិនត្រូវគូរ mismatch ដែលបណ្តាលមកពីបោះចោលបរាជ័យ fail និងមិនត្រឡប់មកវិញ ។ ជំនួស compiler ដែលបោះចោល InvalidCastException ។ ឧទាហរណ៍ នៃ unboxing cast ដែល fails:

Circle c = new Circle(42);

object o = c; // doesn’t box because Circle is a class

int i = (int)o; //compiles okay,but throws