Thứ Sáu, 29 tháng 6, 2012

Khái niệm Coupling và Cohesion trong OOP

Khả năng đóng gói dữ liệu (Data encapsulation), kế thừa (Inheritance) và đa hình (Polymorphism) là nền tảng của lập trình hướng đối tượng, nhưng đơn thuần vận dụng cả ba khái niệm trên không ngẫu nhiên mang lại một thiết kế chương trình tốt. Một chương trình thiết kế theo hướng đối tượng đòi hỏi chia nhỏ bài toán thành các phần, sao cho chúng vừa có đặc tính cố kết (giao tiếp giữa các phần)  lại vừa có khả năng tách biệt riêng rẽ để thay đổi, kiểm tra mà không ảnh hưổng đến thành phần khác. Điều này rất quan trọng trong sự phát triển, tiến hóa của một sản phẩm phần mềm.
Coupling và Cohesion là hai khái niệm trong OOP đặc trưng cho vấn đề này. Coupling : là sự phụ thuộc mô tả mức độ mà theo đó một phương thức (method) hay một kiểu (type) phụ thuộc vào phương thức khác hoặc kiểu khác để thực hiện hành vi của nó. Lớp A được gọi là phụ thuộc ở mức độ cao vào lớp B nếu một sự thay đổi trong lớp B là có khả năng gây ra một thay đổi trong hành vi của lớp A. Coupling còn được hiểu là sự phụ thuộc – dependence.
Các kiểu Coupling được chia làm 2 nhóm : Loosely Coupling và Tightly Coupling. Một chương trình có thiết kế tốt phải đảm bảo được đặc tính Loosely Coupling.
Independent/No Coupling : Nếu 2 lớp hoặc phương thức không chia sẻ bất kỳ dữ liệu nào thì chúng là độc lập (independent). Việc lớp này sử dụng lớp kia một cách đơn giản không có nghĩa là 2 lớp phụ thuộc vào nhau. Ví dụ
    class A
    {
        public void Foo()
        {
            B b = new B();
            b.Bar();
        }
    }
    class B
    {
        public void Bar()
        {
            //Thay doi o day khong anh huong toi hanh vi cua A
        }
    }
Data Coupling : Khi lớp A truyền một giá trị cần thiết cho B, thì A và B được gọi là ràng buộc về dữ liệu (data-coupled).  Đây là kiểu Coupling phổ biến thường được áp dụng. Ví dụ
    class C
    {
        public void Foo()
        {
            string mes = "Hello OOP";
            D d = new D();
            d.Bar(mes);
        }
    }
    class D
    {
        public void Bar(string mes)
        {
            Console.WriteLine(mes);
        }
    }
Stamp Coupling : A truyền cho B kiểu tham chiếu hoặc toàn bộ cấu trúc dữ liệu thay vì chỉ truyền cho B trường dữ liệu cần thiết, do vậy nếu bất kỳ trường hay phương thức nào trong A thay đổi có thể ảnh hưởng tới xử lý của B, hoặc B có thể thay đổi A.
    class G
    {
        public void Foo()
        {
            K k = new K();
            k.Bar(this);
        }
        int field1;
        public int Field1
        {
            get { return field1; }
            set { field1 = value; }
        }
        int field2;
        public int Field2
        {
            get { return field2; }
            set { field2 = value; }
        }
    }
    class K
    {
        public void Bar(G myG)
        {
            //Co the anh huong len myG.Field1 va myG.Field2
        }
    }
Nhận xét : Stamp Coupling khiến chương trình trở lên rối và tạo ra nhiều bug tiềm ẩn, nên tránh truyền toàn bộ cấu trúc hoặc kiểu cho phương thức nếu phương thức đó chỉ sử dụng một phần của cấu trúc.
Tramp Coupling : Các tham số được truyền không đổi qua nhiều phương thức khác nhau cho đến khi chính thức được sử dụng
    class TransactionService
    {
        void CalculateOrder(string custname)
        {
            FetchListProduct(custname);
        }
        void FetchListProduct(string custname)
        {
            FetchAccountInfo(custname);
        }
        void FetchAccountInfo(string custname)
        {
            //Su dung custname o day
        }
    }
Nhận xét : Có thể loại bỏ Tramp Coupling bằng cách đặt custname là một trường trong lớp TransactionService, việc truyền một tham số cho phương thức luôn ngụ ý rằng phương thức đó sử dụng tham số này, do vậy lập trình viên phải tránh viết các phương thức có các tham số đầu vào không có mục đích.
Control Coupling : Nếu logic của lớp B được điều khiển bởi một tham số cho bởi lớp A, thì 2 lớp A và B được gọi là control-coupled. Ở đây điều kiện tiên quyết là A chỉ định logic của B, B không có quyền tự quyết định.
    class AirConditioner
    {
        const int NORMAL_TEMP = 20;
        public void ChangeTemperature()
        {
            int temp = RoomThermometer.RoomTemp;
            if(temp < NORMAL_TEMP)
            {
                IncreaseTemp();
            }
            else
            {
                DecreaseTemp();
            }
        }

        private void DecreaseTemp()
        {
            //throw new NotImplementedException();
        }

        private void IncreaseTemp()
        {
          //throw new NotImplementedException();
        }
    }
    class RoomThermometer
    {
        static int roomTemp;
        public static int RoomTemp
        {
            get { return GetRoomTemp(); }
            set { roomTemp = value; }
        }
        public static int GetRoomTemp()
        {
            Random rand = new Random();
            roomTemp = rand.Next(0, 50);
            return roomTemp;
        }
    }
Nhận xét:  đây là kiểu Coupling được sử dụng phổ biến trong lập trình.
External Coupling : Nếu 2 lớp A và B tương tác thông qua một non-native object (file, CSDL), thì 2 lớp đó được gọi là external coupled. Yếu điểm ở đây là khó theo dõi các trạng thái giao tiếp, debug gặp khó khăn.
Common Coupling : Nếu 2 lớp A và B là phụ thuộc vào cùng một dữ liệu tĩnh (static data), thì 2 lớp đó được gọi là common coupled.
Content Coupling : Nếu 2 lớp A và B thay đổi trực tiếp trạng thái nội tại của lớp kia, dẫn tới thay đổi hành vi của nó thì được gọi là content coupling

Cohesion và Coupling


Khi học môn Công nghệ phần mềm, cohesion và coupling là hai khái niệm mà HM đôi khi cảm thấy hơi lẫn lộn. Hi vọng bài viết này sẽ phần nào giúp những người cùng cảnh ngộ phân biệt chúng rõ ràng hơn.
Trước hết, hãy nói về nguyên tắc chung khi thiết kế chương trình:
  1. Giữ những thứ phải thay đổi cùng nhau ở thật gần nhau. Chẳng hạn khi thay đổi về thiết kế ở class A luôn kéo theo thay đổi ở B, thì nên cho A và B vào cùng một module.
  2. Cho phép những thứ không liên quan thay đổi một cách độc lập với nhau (nói cho có vẻ “chuyên ngành” là orthogonal). Ví dụ không có lí do gì để sửa đổi class Person khi class Money bị sửa đổi.
Từ hai nguyên tắc này có thể đẻ ra cả tá nguyên tắc và khái niệm cụ thể hơn, mà trong đó có cohesion và coupling. Tuy nhiên, nếu nắm vững hai nguyên tắc trên thì trong trường hợp không biết về các khái niệm khác, bạn vẫn có thể nhận ra những bất thường trong thiết kế.
Vậy cohesion là gì? Khi nói đến cohesion chúng ta nghĩ đến nhiệm vụ của từng module. Nhiệm vụ của từng module càng rõ ràng và tách biệt thì cohesion càng cao (high cohesion), và đó là mục tiêu cần đạt tới khi thiết kế. Giải thích bằng code có lẽ sẽ không rõ ràng bằng bài viết này:
Tại kỳ họp Quốc hội thứ năm, khi thảo luận về quản lý chất lượng vệ sinh an toàn thực phẩm có vị đại biểu Quốc hội đã ví việc có tới 5 bộ chịu trách nhiệm chính như vậy cũng giống như “nhiều sãi không ai đóng cửa chùa”.
Bởi thế, làm rõ trách nhiệm của từng cơ quan quản lý Nhà nước về an toàn thực phẩm là một yêu cầu được nhấn mạnh khi xây dựng dự Luật An toàn thực phẩm.
Thực ra không có một phương pháp tuyệt đối để xác định những nhiệm vụ nào thì được coi là liên quan và nên được xếp vào cũng một module, hay những nhiệm vụ nào là không phù hợp với một module nào đó. Tuy nhiên, có một cách rất hữu dụng, là kiểm tra tên của một class hay method. Nếu bạn không thể kiếm một cái tên phù hợp cho class, bạn nên kiểm tra xem nhiệm vụ của module tương ứng đã được định nghĩa rõ ràng chưa. Cũng vậy, nếu tên của một method có từ and hoặc chứa hai hành động trở lên, thì bạn nên xem xét phân tách method thành nhiều method nhỏ hơn. Về các lời khuyên liên quan đến cách đặt tên, xem thêm Code complete.
Tiếp theo là coupling. Coupling là khái niệm chỉ độ phụ thuộc giữa các module với nhau khi thực hiện một chức năng nào đó. Cũng như cohesion, coupling được chia làm nhiều cấp độ, và một thiết kế tốt sẽ cho coupling thấp (loose coupling). Thế nào là phụ thuộc? Đó là khi một module khi hoạt động phải hiểu biết về chức năng và cách hoạt động của các module khác. Chẳng hạn, khi một class dùng chung một vùng nhớ (một object chẳng hạn) với class khác, thì class này phải hiểu rõ class kia sẽ làm gì với vùng nhớ chung đó, và vậy là có coupling giữa hai class. Có rất nhiều dạng coupling, có dạng chấp nhận được, có dạng nên hạn chế, bạn có thể xem thêm trong Code complete.
Lí do phải giảm coupling là vì nếu nhiều module couple chặt với nhau, thì khi bạn thiết kế lại một module, bạn sẽ phải kiểm tra tất cả các module còn lại, và coupling càng chặt thì khả năng các module còn lại này bị thay đổi càng cao. Ngoài ra, có một số dạng coupling ngầm tương đối khó nhận ra, điều này sẽ làm cho việc thay đổi càng khó khăn hơn. Bạn có thể liên hệ với thủ tục hành chính. Nếu người dân phải đi qua nhiều bước thủ tục thì khi các bước này bị thay đổi, người ta phải tuyên truyền lại cho nhiều chục triệu người dân lẫn nhân viên hành chính. Nếu các bước thủ tục này được encapsulate lại thành một bước duy nhất (“một cửa”) thì người ta chỉ phải đến, đưa hồ sơ (“input”) và lấy kết quả (“output”). Thay đổi về thủ tục bây giờ chỉ đòi hỏi đào tạo lại các nhân viên bên trong.
Bạn có thể thấy cohesion và coupling phần nào phản ánh hai nguyên tắc ban đầu.

Tìm hiểu về Stack và Heap trong C# ( phần 3)


Một bản sao nhưng không phải là một bản sao!
Để giải thích rõ hơn vấn đề này chúng ta sẽ xem điều gì sẽ xảy ra khi một kiểu giá trị một kiểu tham chiếu cùng ở trên heap. Đầu tiên chúng ta hãy tìm hiểu về kiểu giá trị. Chúng ta có một class Dude với các thuộc tính là Name và 2 Shoe(s), hàm CopyDude() để dễ dàng tạo ra những đối tượng Dudes mới.
           public struct Shoe{
               public string Color;
           }

           public class Dude
           {
                public string Name;
                public Shoe RightShoe;
                public Shoe LeftShoe;

                public Dude CopyDude()
                {
                    Dude newPerson = new Dude();
                     newPerson.Name = Name;
                     newPerson.LeftShoe = LeftShoe;
                     newPerson.RightShoe = RightShoe;

                     return newPerson;
                }

                public override string ToString()
                {
                     return (Name + " : Dude!, I have a " + RightShoe.Color  +
                         " shoe on my right foot, and a " +
                          LeftShoe.Color + " on my left foot.");
                }

           }
Class Dude là một kiểu tham chiếu ó Struct Shoe là một phần tử của class nên cả 2 đều được lưu trong Heap.
Chúng ta chạy hàm dưới đây:
           public static void Main()
           {
               Class1 pgm = new Class1();

                  Dude Bill = new Dude();
                  Bill.Name = "Bill";
                  Bill.LeftShoe = new Shoe();
                  Bill.RightShoe = new Shoe();
                  Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue";

                  Dude Ted =  Bill.CopyDude();
                  Ted.Name = "Ted";
                  Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red";

                  Console.WriteLine(Bill.ToString());
                  Console.WriteLine(Ted.ToString());            

           }
Chúng ta sẽ nhận được kết  quả sau:
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot.
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot.
Nếu chúng ta chuyển Shoe thành một kiểu tham chiếu như sau:
           public class Shoe{
               public string Color;
           }
Và chạy y như vậy trong Main(), hãy xem dữ liệu ra của chúng ta thay đổi như thế nào:
Bill : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Cái giầy đỏ ở chân bên kia. Đây rõ ràng là một lỗi. Bạn có hiểu tại sao như vậy ko? Đây là những gì chúng ta lưu trên heap.
Bời vì bây giờ chúng ta dùng Shoe như một kiểu tham chiếu thay cho kiểu giá trị và khi nội dung của kiểu tham chiếu được copy thì chỉ có con trỏ được copy ( chứ không phải đối tượng được trỏ tới). chúng ta phải làm thêm một số công việc để làm cho biến tham chiếu Shoe giống với kiểu giá trị.
Thật may là chúng ta có một interface giúp chúng ta làm được điều này đó là: Icloneable. Interface này về cơ bản là một quy định làm cho các Dude có thể được sao lưu. Tất cả các thành phần trong Class đều cần được copy lại nên chúng ta sử dụng Icloneable interface. Và Icloneable có chứa một hàm là Clone().
ICloneable consists of one method: Clone()
                  public object Clone()
                  {

                  }
Đây là cách chungs ta thực thi nó trong class Shoe:
           public class Shoe : ICloneable
             {
                  public string Color;
                  #region ICloneable Members

                  public object Clone()
                  {
                      Shoe newShoe = new Shoe();
                      newShoe.Color = Color.Clone() as string;
                      return newShoe;
                  }

                  #endregion
             }
Trong phương thức Clone(), chúng ta chỉ tạo ra một đối tượng Shoe mới, copy toàn bộ kiểu tham chiếu và kiểu giá trị rồi trả về một đối tượng mới. Các bạn nên nhớ một lớp string thực thi Icloneable nên chúng ta phải gọi Color.Clone(). Bời vì hàm Clone biến một kiểu tham chiếu thành một đối tượng, nên chúng ta phải định nghĩa lại kiểu cho nó trước khi chúng ta thiết lập thuộc tính color cho Shoe.
Tiếp theo, trong phương thức CopyDude() chúng ta cần tạo lại shoes thay vì copy nó.
                public Dude CopyDude()
                {
                    Dude newPerson = new Dude();
                     newPerson.Name = Name;
                     newPerson.LeftShoe = LeftShoe.Clone() as Shoe;
                     newPerson.RightShoe = RightShoe.Clone() as Shoe;

                     return newPerson;
                }
Và sau đây chúng ta thực hiện hàm Main()
           public static void Main()
           {
               Class1 pgm = new Class1();

                  Dude Bill = new Dude();
                  Bill.Name = "Bill";
                  Bill.LeftShoe = new Shoe();
                  Bill.RightShoe = new Shoe();
                  Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue";

                  Dude Ted =  Bill.CopyDude();
                  Ted.Name = "Ted";
                  Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red";

                  Console.WriteLine(Bill.ToString());
                  Console.WriteLine(Ted.ToString());            

           }
Chúng ta nhận được kết quả như sau:
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Thông thường chúng ta muốn tạo mới kiểu tham chiếu và copy kiểu giá trị. Điều đó giúp bạn dễ dàng quản lý hơn và tránh lỗi tốt hơn. Chúng ta hãy thử làm cho class Dude “sạch sẽ” hơn để thực thi Iccloeable thay vì sử dụng hàm CopyDude()
           public class Dude: ICloneable
           {
                public string Name;
                public Shoe RightShoe;
                public Shoe LeftShoe;

                public override string ToString()
                {
                     return (Name + " : Dude!, I have a " + RightShoe.Color  +
                         " shoe on my right foot, and a " +
                          LeftShoe.Color + " on my left foot.");
                    }
                  #region ICloneable Members

                  public object Clone()
                  {
                       Dude newPerson = new Dude();
                       newPerson.Name = Name.Clone() as string;
                       newPerson.LeftShoe = LeftShoe.Clone() as Shoe;
                       newPerson.RightShoe = RightShoe.Clone() as Shoe;

                       return newPerson;
                  }

                  #endregion
             }
           public static void Main()
           {
               Class1 pgm = new Class1();

                  Dude Bill = new Dude();
                  Bill.Name = "Bill";
                  Bill.LeftShoe = new Shoe();
                  Bill.RightShoe = new Shoe();
                  Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue";

                  Dude Ted =  Bill.Clone() as Dude;
                  Ted.Name = "Ted";
                  Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red";

                  Console.WriteLine(Bill.ToString());
                  Console.WriteLine(Ted.ToString());            

           }
Và kết qủa cuối cùng là:
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot.
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot.
Các bạn hãy down tài liệu kèm theo trong bài viết này để hiểu kĩ hơn.

Tìm hiểu về Stack và Heap (phần 2)


Bài viết này sẽ trình bày chi tiết hơn về việc thi hành code. Ở bài viết trước chúng ta đã cơ bản hiểu được điều gì xảy ra trong bộ nhớ khi gọi một hàm. Và bài này các bạn sẽ được tìm hiểu kĩ hơn.
Khi chúng ta gọi một hàm thì những việc sau sẽ được thực hiện:
1. Stack sẽ Cấp phát khoảng trống cho những thông tin mà hàm cần đến để xử lý (Được gọi là Stack Frame). Ô nhớ được cấp phát này có cả địa chỉ gọi (một con trỏ) ví dụ đơn giản như là một chỉ dấn GOTO- giúp chương trình của bạn biết nơi gọi các lệnh tiếp theo khi kết thúc một công việc nào đó.
2. Các tham số của hàm sẽ được copy. Và điều này tôi sẽ trình bày kĩ trong bài.
3. Điều khiển sẽ khớp với hàm JIT’ted và thread bắt đầu thi hành code. Sau đấy sẽ có các hàm khác chèn vào stack và được gọi.
Các bạn hãy đọc đoạn code sau:
public int AddFive(int pValue)
{
int result;
result = pValue + 5;
return result;
}
Đây là những gì diễn ra trong stack
Như ở phần 1 tôi đã trình bày việc sắp đặt các tham số trong stack khá phức tạp còn tùy thuộc vào nó là kiểu tham trị hay kiểu tham chiếu. Một kiểu giá trị thì được copy lại nhưng kiểu tham chiếu đến một tham chiếu thì được copy đi copy lại :D .
Đầu tiên chúng ta cùng tìm hiểu về kiểu truyền tham trị
Khi chúng ta truyền tham trị, ô nhớ được cấp phát và giá trị của tham trị đó sẽ được copy vào một ô nhớ mới trong stack. Theo dõi đoạn code sau:
class Class1
{
public void Go()
{
int x = 5;
AddFive(x);
Console.WriteLine(x.ToString());
}
public int AddFive(int pValue)
{
pValue += 5;
return pValue;
}
}
Theo hàm này thì ô nhớ cho biến X giờ nhận giá trị là 5.
Tiếp đến hàm AddFive() sẽ được đặt vào một ô trong stack chứa các tham số và giá trị được copy lại dần dần từ x.
Khi hàm AddFive() kết thúc, thread sẽ gọi đến hàm Go và vì hàm AddFive() đã thực hiện xong nên, pValue về cơ bản đã bị “removed”.
Kết quả đưa ra màn hình là 5 Có nghĩa là mọi tham trị trong hàm là bản sao vào chúng ta hi vọng giá trị gốc sẽ được giữ lại.
Một điều bạn nên ghi nhớ đấy là nếu chúng ta có một kiểu tham trị lớn như là một struct phức tạp chẳng hạn và đưa nó vào stack, Điều này gây lãng phí bộ nhớ và chu trình xử lý để copy nó. Stack không phải là một không gian nhớ vô hạn nó cũng giống như một cái cốc có đáy đặt dưới vòi nước, đương nhiên là nó có thể bị tràn. Struct là một kiểu giá trị có thể nhận những giá trị khá lớn vì thế chúng ta cần nắm vững cách xử lý nó.
Ví dụ một struct khá lớn:
public struct MyStruct
{
long a, b, c, d, e, f, g, h, i, j, k, l, m;
}
Tưởng tượng điều gì xảy ra khi chúng ta gọi hàm Go() và thực hiện thêm hàm DoSomething() dưới đây:
public void Go()
{
MyStruct x = new MyStruct();
DoSomething(x);
}
public void DoSomething(MyStruct pValue)
{
// DO SOMETHING HERE….
}
Cách này thực sự là không khả thi. Hãy hình dung chúng ta gấp đôi MyStruct khoảng 1000 lần và bạn có thể hiểu điều gì xảy ra rồi đấy.
Vậy giải quyết vấn đền này thế nào? Chúng ta sẽ sử dụng tham chiếu đến kiểu giá trị gốc như sau:
public void Go()
{
MyStruct x = new MyStruct();
DoSomething(ref x);
}
public struct MyStruct
{
long a, b, c, d, e, f, g, h, i, j, k, l, m;
}
public void DoSomething(ref MyStruct pValue)
{
// DO SOMETHING HERE….
}
Có một điều duy nhất các bạn cần lưu ý là sử dụng kiểu tham trị bằng cách tham chiếu là bạn phải truy cập đến giá trị của kiểu. Mỗi khi thay đổi pValue tức là chúng ta thay đổi x. Sử dụng đoạn code dưới đây, Kết quả trả về sẽ là “12345” bởi vì pValue.a trên thực tế đã tìm đến ô nhớ mà biến x ban đầu được định nghĩa
public void Go()
{
MyStruct x = new MyStruct();
x.a = 5;
DoSomething(ref x);
Console.WriteLine(x.a.ToString());
}
public void DoSomething(ref MyStruct pValue)
{
pValue.a = 12345;
}
Sử dụng kiểu truyền tham chiếu
Trong ví dụ sau, Sử dụng biến kiểu tham chiếu nó cũng giống với việc bạn dùng tham trị nhưng truy cập theo kiểu tham chiếu:
Nếu chúng ta sử dụng tham trị:
public class MyInt
{
public int MyValue;
}
Sau đó gọi đến Go(), lớp MyInt sẽ được lưu trên Heap vì nó là kiểu tham chiếu
public void Go()
{
MyInt x = new MyInt();
}
Nếu chúng ta gọi hàm Go() như sau
public void Go()
{
MyInt x = new MyInt();
x.MyValue = 2;
DoSomething(x);
Console.WriteLine(x.MyValue.ToString());
}
public void DoSomething(MyInt pValue)
{
pValue.MyValue = 12345;
}
Những gì diễn ra trong stack và heap :
1. đầu tiên trong Go() biến x được đưa vào stack.
2. Khi chúng ta gọi hàm DoSomething() biến pValue được đưa vào stack.
3. giá trị của x (địa chỉ của MyInt trong stack) được copy vào pValue.
Có nghĩa là khi chúng ta thay đổi MyValue của đối tượng MyInt trong Heap sử dụng pValue sau đấy gọi đến đối tượng trong Heap sử dụng x, chúng ta sẽ nhận được giá trị là “12345”
Vậy điều gì sẽ xảy ra khi chúng ta tham chiếu sử dụng tham biến.
Hãy kiểm tra class sau. Chúng ta có 2 class là Animal và Vegetables đều kế thừa lơp things.
public class Thing
{
}
public class Animal:Thing
{
public int Weight;
}
public class Vegetable:Thing
{
public int Length;
}
Và chúng ta thực hiện hàm Go() như sau:
public void Go()
{
Thing x = new Animal();
Switcharoo(ref x);
Console.WriteLine(
“x is Animal : “
+ (x is Animal).ToString());
Console.WriteLine(
“x is Vegetable : “
+ (x is Vegetable).ToString());
}
public void Switcharoo(ref Thing pValue)
{
pValue = new Vegetable();
}
Biến X sẽ được trả về một Vegetable.
x is Animal : False
x is Vegetable : True
1. đầu tiên khi gọi hàm Go(), con trỏ x sẽ được đua vào stack.
2. Đối tượng Animal được đưa vào Heap.
3. Khi gọi hàm Switchroo(), pValue được đưa vào stack và trỏ đến x.
4. Đối tượng Vegetable được đưa vào Heap.
5. con trỏ x thay đổi giá trị theo pValue và trỏ đến Vegetable
Nếu chúng ta không sử dụng biến ref, chúng ta sẽ giữ lại Animal và sẽ nhận được giá trị ngược lại.