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

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.

Không có nhận xét nào: