Lập trình Windows - Chương 2: Ngôn ngữ lập trình C# - Phần 1

Định danh – Identity: Tên lớp, tên phương thức, tên biến, tên đối tượng, tên hằng, tên kiểu, Quy tắc tạo định danh trong C#: Ký tự đầu tiên: chữ, ký tự gạch dưới, ký tự @ Các ký tự còn lại: chữ, số, ký tự gạch dưới Có thể dùng @ ở đầu từ khóa để tạo định danh

pptx141 trang | Chia sẻ: huyhoang44 | Lượt xem: 664 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Lập trình Windows - Chương 2: Ngôn ngữ lập trình C# - Phần 1, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Lập trình Windows Chương 2. Ngôn ngữ lập trình C# Phần 11Nội dungTổng quan C#Các thành phần cơ bản của ngôn ngữHệ thống kiểu trong .NETNamespace Các câu lệnhLớpThừa kếProperty, Mảng và IndexerLớp CollectionInterfaceDelegate và event handlerXử lý Ngoại lệ2Tổng quan C#Tổng quan C#C# (C-Sharp) là ngôn ngữ lập trình do Microsoft sáng tạo ra dựa trên những ưu điểm của C++, Java, Smalltalk và bổ sung thêm những phần mớiCác phiên bản ngôn ngữ C#C# 1.0 cho .NET Framework 1.0 (1.1)C# 2.0 cho .NET Framework 2.0 (3.0)C# 3.0 cho .NET Framework 3.5C# 4.0 cho .NET Framework 4.0C# 5.0 cho .NET Framework 4.5File source code C# có phần mở rộng .cs. Một chương trình gồm có một hay nhiều file source codeTổng quan C#Mục tiêu thiết kế C#Ngôn ngữ hướng thành phần (Component-orientation)Mọi thứ đều là đối tượngTạo ra phần mềm mạnh và bền Ngôn ngữ hướng thành phầnNgôn ngữ hướng thành phần đầu tiên trong họ C/C++Khái niệm hướng thành phầnProperties, methods, eventsDesign-time và run-time attributesTích hợp documentation bằng XMLCho phép one-stop programmingKhông header files, IDL, Có thể nhúng trong các trang ASP.NETMọi thứ đều là đối tượngQuan điểm truyền thốngC++, Java™: Các kiểu cơ sở (Primitive type) không thể tương tác với các objectSmalltalk, Lisp: Các kiểu cơ sở là các object, nhưng phải trả giá về chi phí thực thiC# thống nhất 2 loại kiểu nhưng không phải trả giá về chi phí thực thiTăng cường các kiểu dữ liệu khácCác kiểu cơ sở mới : Decimal, SQLCollections, làm việc trên tất cả các kiểuTạo ra phần mềm mạnh và bềnGarbage collection (GC)Không bị rò rỉ bộ nhớ và các con trỏ không được truy cập bất hợp lệNgoại lệ (Exception)Cho phép xử lý các ngoại lệAn toàn kiểu (Type-safety)Không được dùng các biến chưa khởi tạo, ép kiểu (cast) không an toànChương trình C# đầu tiênusing System; class Program { static void Main(string[] args) { Console.WriteLine("Hello, World"); } }1234LớpMột ứng dụng C# gồm tập các class và structMột lớp gồm tập dữ liệu và phương thứcCú phápclass ClassName { }Phương thức MainPhương thức Main được định nghĩa trong lớpChú ý khi viết hàm MainKý tự M phải viết HOA, “Main”Phải có một hàm Main là entry point của chương trìnhKhai báo Main: static void MainKhi hàm Main kết thúc hay gặp lệnh return thì ứng dụng kết thúcDùng Directive và System namespace.NET Framework cung cấp nhiều lớp tiện íchCác lớp được tổ chức thành các namespaceSystem là namespace được dùng thông dụng nhấtKhi sử dụng lớp phải chỉ rõ lớp đó thuộc namespace nàoSystem.Console.WriteLine("Hello, World");using System;Console.WriteLine("Hello, World");Dùng directiveXuất dữ liệuNhập dữ liệu từ bàn phím và xuất dữ liệu ra màn hình trong C# có thể dùng các phương thức tĩnh trong lớp: System.Consolevoid Console.Write(data);void Console.WriteLine(data);Xuất dữ liệu lên màn hìnhCú pháp 1:Xuất dữ liệuCú pháp 2:void Console.Write(string format, params object[] arg);void Console.WriteLine(string format, params object[] arg);Trong đó:format: chứa chuỗi định dạngarg là mảng các đối tượng sẽ được xuất ra theo chuỗi định dạngXuất dữ liệuformat là một chuỗi bình thường và có thể có thêm một hay nhiều phần định dạng có cú pháp sauCú pháp:{index[,alignment][:formatString]}Trong đó:index: Số thứ tự của đối số, bắt đầu từ 0alignment: độ rộng, M>0 canh phải, M>Quan hệ== != =Gán= += -= *= /= %= &= |= ^= >= ?? (3.0)Truy cập thành viên.Chỉ mục, chỉ số[]Ép kiểu()Điều kiện?:Nối và gở bỏ delegate+= -=Tạo đối tượngnewThông tin kiểuas is sizeof typeofĐiều khiển ngoại lệ tràng bộ nhớchecked uncheckedIndirection và Address* -> [] & Lambda=> (3.0)Chú thích Giống C/C++///* */XML Comment/// /// /// /// Hệ thống kiểu .NETCommon Type System – CTSCommon Type System – CTSMô tả các kiểu được định nghĩa như thế nào và chúng hoạt động ra sao. CTS cung cấp các kiểu dữ liệu cơ sở mà các ngôn ngữ trên .NET Framework phải dùng. Với mục đích các chương trình được viết bằng những ngôn ngữ khác nhau dễ dàng chia sẽ thông tin với nhauCommon Type System – CTSMục đích của CTSCho phép .NET Framework tích hợp nhiều ngôn ngữ khác nhauXác định các quy tắc của các kiểu mà các ngôn ngữ lập trình trên .NET phải tuân theo cho nên các đối tượng được viết bằng những ngôn ngữ khác nhau có thể tương tác với nhauCommon Type System – CTSPhân loại CTSKiểu giá trị (value type)Kiểu giá trị chứa trực tiếp giá trị của nó và các instance của kiểu giá trị được cấp phát trên vùng stackKiểu tham chiếu (reference type)Lưu tham chiếu đến vùng nhớ của giá trị (đối tượng), vùng nhớ của giá trị được cấp phát trên heapCác biến kiểu tham chiếu chứa giá trị null để báo rằng không tham chiếu đến đối tượng nào trên vùng nhớ heap Common Type System – CTSKiểu cơ sở trong CTS (build-in type) và Bí danhCTS định nghĩa các kiểu cơ sở cho tất cả các ngôn ngữ lập trình trên .NET như: System.Int32, System.String, Mỗi ngôn ngữ thường định nghĩa các bí danh cho các kiểu cơ sở trong CTSVí dụ:CTS định nghĩa System.Int32 – 4 byte số nguyênC# định nghĩa int là bí danh của System.Int32C# định nghĩa string là bí danh của System.String.Kiểu cơ sở trong CTS (build-in type) và Bí danhKiểu trong C# và kiểu trong .NET Framework có thể dùng thay thế cho nhauBí danh trong C#Kiểu cơ sởtrong CTSboolSystem.BooleanbyteSystem.BytesbyteSystem.SBytecharSystem.ChardecimalSystem.DecimaldoubleSystem.DoublefloatSystem.SingleintSystem.Int32uintSystem.UInt32longSystem.Int64ulongSystem.UInt64shortSystem.Int16ushortSystem.UInt16stringSystem.String objectSystem.ObjectKiểu chuỗiĐặc điểm stringChuỗi ký tự unicodeKiểu tham chiếuKích thước cố địnhHằng chuỗi:“”@“”Phép toán trên stringToán tử ==, != dùng để so sánh các giá trị của string+, []Chuyển kiểu ngầm địnhKhông có chuyển kiểu ngầm định Sang kiểu charGiữa số thực và decimalBoxing và UnboxingVấn đề: Việc tách thành hai loại kiểu (kiểu giá trị và kiểu tham chiếu) thì làm thế nào các kiểu tương tác với nhau?Giải pháp: Dùng 2 kỹ thuật sau đâyBoxing: là chuyển từ kiểu giá trị sang kiểu tham chiếuUnboxing: là chuyển từ kiểu tham chiếu sang kiểu giá trị Boxing và UnboxingBoxingVí dụ: int a = 55;object o = a;Quá trình hoạt động của boxingTrước hết một vùng nhớ được cấp phát trên vùng nhớ heap để tạo đối tượng oSau đó giá trị của biến kiểu giá trị được sao chép sang vùng nhớ heap đóCuối cùng địa chỉ của đối tượng được cấp phát trên heap được đặt vào vùng nhớ trên stackBoxing và UnboxingUnboxingVí dụ: int a = 55;object o = a;int b = (int)o;Chú ý: Boxing là không cần ép kiểu Unboxing phải ép kiểuBoxing và UnboxingQuá trình hoạt động của unboxingTrước hết runtime kiểm tra xem địa chỉ trên stack có trỏ đến đối tượng hợp lệ không và kiểm tra xem kiểu đối tượng có thể được chuyển sang kiểu giá trị không. Nếu không sẽ ném ra một ngoại lệ InvalidCastExceptionMột con trỏ đến giá trị bên trong đối tượng được trả về. Chú ý rằng boxing tạo một bản sao của kiểu được chuyển đổi, còn unboxing thì không làm thế. Namespace NamespaceĐược dùng để định nghĩa một phạm vi (scope). Phạm vi namespace này cho phép chúng ta tổ chức code và tạo ra các kiểu có tên duy nhấtCú pháp:namespace  {        }NamespaceTrong namespace chúng ta có thể khai báo một hay nhiều kiểu sau đâyNamespace khácClassInterfaceStructEnumDelegateNamespacenamespace MyProject {  class Class1 {} class Class2 {} }namespace MyCompany { namespace Project1 { class Class1 {} class Class2 {} } namespace Project2 { class Class1 {} class Class2 {} } }NamespaceCác namespace có những đặc điểm sauCác namespace được dùng để tổ chức các project lớnCác namespace được cách bằng toán tử “.”Có thể định nghĩa 1 namespace trong hai hay nhiều khai báoNamespce “global” là namespace gốc cho các namespace (gọi là default namespace hay global namespace)global::System.Console.WriteLine();Namespacenamespace MyCompany.Project1 { class Class1 {} class Class2 {} } namespace MyCompany.Project2 { class Class1 {} class Class2 {} }namespace MyCompany { namespace Project1 { class Class1 {} class Class2 {} } namespace Project2 { class Class1 {} class Class2 {} } }Namespacenamespace MyCompany.Project1 { class Class1 {} class Class2 {} } namespace MyCompany.Project1 { class Class3 {} class Class4 {} }Chú ý: có thể có nhiều tập tin mã nguồn dùng để phân phối cho 1 namespaceNamespace Từ khóa usingĐể truy cập các thành phần bên trong namespace chúng ta dùng Cú pháp.using .;Để viết code dễ dàng hơn chúng ta có thể dùng câu lệnh usingCú phápNamespace Từ khóa usingTừ khóa using cho phép xác định trật tự tìm kiếm các namespace khi trình biên dich gặp một kiểu mà không có tên namespace đứng trước Từ khóa using không dùng để xác định tên lớpusing System.Console;class Program { static void Main(string[] args) { WriteLine("Hello, World"); } }Namespace Từ khóa usingDùng từ khóa using để tạo bí danh cho lớpusing console = System.Console;class Program { static void Main(string[] args) { console.WriteLine("Hello, World"); } }Các lệnh điều khiển: if, switch, goto, for, while, dowhile, foreachCâu lệnh if, switchCú pháp 1if (expression) { Các câu lệnh }if (expression) { Các câu lệnh A; } else { Các câu lệnh B; }Cú pháp 2Quy tắc của câu lệnh if: Giá trị của biểu thức phải là một giá trị kiểu boolCâu lệnh if, switchCâu lệnh switchCú pháp switch (expression) { case const_expression1: break; case const_expression2: break; case const_expressionN: break; default: break; }Câu lệnh if, switchQuy tắc của câu lệnh switch:expression phải thuộc một trong các kiểu: số nguyên, char, string, enum Mỗi case (kể cả default) luôn cung cấp “lệnh nhảy” (jump statement) (break, return, goto)Nếu thân case là câu lệnh rỗng thì không cần “lệnh nhảy”Thứ tự các case, default không quan trọng Câu lệnh nhảy – jump statementCâu lệnh break, continue, return giống như trong C/C++Câu lệnh gotoCú phápgoto label;goto case constExpressiongoto default;Quy tắc của câu lệnh nhảy: được nhảy ra, không được nhảy vàoCâu lệnh lặpCâu lệnh forCú pháp for (initialization; BooleanExpression; step) { Các câu lệnh }while (BooleanExpression) { Các câu lệnh }Câu lệnh whileCú phápCâu lệnh lặpCâu lệnh do...whileCú pháp do { Các câu lệnh } while (BooleanExpression);Câu lệnh lặpCâu lệnh foreachCú pháp foreach (type variableName in expression) { Các câu lệnh }Trong đó:type là kiểu của biến variableNameexpression là đối tượng thuộc collection hay mảngLớp Định nghĩa lớpĐịnh nghĩa lớpCú pháp:[attributes] [modifiers] class [:BaseClassName] {     [class-body] }[;]12Định nghĩa lớpVí dụclass NhanVien { private long maNV; }class MyClass { // members }class MyClass { // members };Không cần thiết phải “;” để kết thúc lớp. Access modifierThân của lớpMột lớp trong C# có thể chứa các loại thành viên ConstantsFieldsMethods OperatorsPropertiesEventsIndexersInstance constructors, static constructors Destructors Các kiểu khai báo lồng nhau (class, struct, interface, enum, delegate)Thân của lớppublic class First { const int MAX = 5; string name; public int Say(string message) { return message + “ ” + name; } public class Second { } }Kiểu ở mức cao nhất (top-level)Kiểu bị lồng (nested)Bổ từ truy cập Cho kiểu ở mức cao nhấtCó 2 bổ từ truy cập cho kiểu ở mức cao nhấtpublicinternalDEFAULT: internalpublic class AccessTypeInCSharp {      } class AccessInCSharp {      } internal class AccessInCSharp {      } Bổ từ truy cập Cho thành viênCó 5 bổ từ truy cập cho thành viênpublicprotectedprivateinternalinternal protectedDEFAULT: privateclass AccessMembersInCSharp {      public int a; public int b; public int c; protected int d; protected int e; } Bổ từ truy cập Cho thành viênclass AccessMembersInCSharp { public: int a; int b; int c; protected: int d; int e; };class AccessMembersInCSharp {      public int a; public int b; public int c; protected int d; protected int e; } C++C#class AccessMembersInCSharp {      protected int a; int b; } Bổ từ truy cập Cho thành viênNếu kiểu có bổ từ truy cập là internal (hay không có bổ từ truy cập) thì các thành viên của kiểu này có thể khai báo 3 cấp độ truy cập (giống C++)public = internal = internal protectedprotectedprivateNếu kiểu có bổ từ truy cập là public thì các thành viên của kiểu này có thể khai báo 5 cấp độ truy cậppublicprotectedprivateinternalinternal protectedThân của lớp FieldFieldMột field là một biến thành viên dùng để lưu giữ giá trị của một đối tượngCú pháp: ;Trong đó có thể làKiểu cơ sở: char, int, float, double, EnumStructClass Delegate Thân của lớp FieldQuy tắc về khai báo FieldField có thể là một đối tượng của lớp đang định nghĩa Có thể vừa khai báo, vừa khởi tạo dữ liệu cho fieldThân của lớp Phương thức Phương thức và tham sốTrong C#, khái niệm phương thức (method) và hàm (function) là đồng nghĩa với nhau. Phương thức là đoạn mã thao tác trên các field. Trong C#, định nghĩa phương thức theo quy tắcPhương thức phải nằm trong class hay structThân của phương thức nằm trong định nghĩa lớp (nghĩa là không có sự phân biệt giữa khai báo và định nghĩa phương thức)Thân của lớp Phương thứcclass ClassName { Method(int data) { } } class Program { static void Main() { int x = 7; ClassName obj = new ClassName(); obj.Method(x); } }Tham số hình thứcTham số thựcThân của lớp Phương thứcCác tham số của phương thức: có 2 loại tham sốTham số giá trị (Value parameter – tham trị): Khi gọi phương thức có tham số giá trị thì chúng ta đang gởi một bản sao của tham số thực cho phương thức (bất kỳ thay đổi dữ liệu của tham số trong phương thức cũng không ảnh hưởng đến dữ liệu ban đầu được lưu trong tham số thực)Đây là cách truyền mặc định Thân của lớp Phương thứcclass SomeClass { public void Change(int data) { data = data*2; } } class Program { static void Main() { SomeClass obj = new SomeClass(); int x=7; obj.Change(x); Console.WriteLine(“x=” + x); } }Tham trị với kiểu giá trịThân của lớp Phương thứcclass AnotherClass { public int data; } class SomeClass { public void Change(AnotherClass obj) { obj = new AnotherClass(); obj.data = 100; } } class Program { static void Main() { AnotherClass at = new AnotherClass() SomeClass sc = new SomeClass(); at.data = 7; sc.Change(at); Console.WriteLine(“data=” + at.data); } }Tham trị với kiểu tham chiếuThân của lớp Phương thứcclass AnotherClass { public int data; } class SomeClass { public void Change(AnotherClass obj) { obj.data = obj.data*2; } } class Program { static void Main() { AnotherClass at = new AnotherClass() SomeClass sc = new SomeClass(); at.data = 7; sc.Change(at); Console.WriteLine(“data=” + at.data); } }Tham trị với kiểu tham chiếuThân của lớp Phương thứcclass SomeClass { public void Change(string data) { data = “New String”; } } class Program { static void Main() { SomeClass obj = new SomeClass(); string s=“Old String”; obj.Change(s); Console.WriteLine(“s=” + s); } }Tham trị với kiểu giá trịThân của lớp Phương thứcTham số tham chiếu (tham chiếu): Khi gọi phương thức có tham số tham chiếu, thì tham số thực và tham số hình thức đều chỉ đến cùng ô nhớ (bất kỳ thay đổi dữ liệu của tham số trong phương thức cũng ảnh hưởng đến dữ liệu ban đầu được lưu trong tham số thực)Thêm modifier: ref hay outThân của lớp Phương thứcclass ClassName { void Square1(int x) {} void Square2(ref int x) {} void Square3(out int x) }Thân của lớp Phương thứcPhương thức với tham số refCú pháp:class ClassName { Method(ref type variableName) { } }Thân của lớp Phương thứcCú pháp: sử dụng phương thức có tham số ref variableName = value; Method(ref variableName);Chú ý Tham số thực phải được khởi tạo trước khi gọi phương thức.Mọi thay đổi giá trị trong tham số hình thức đều thay đổi giá trị trong tham số thực.Thân của lớp Phương thứcclass SomeClass { public void HoanVi(ref int x, ref int y) { int tam = x; x = y; y = tam; } } class Program { static void Main() { SomeClass obj = new SomeClass(); int x=7, y=9; obj.HoanVi(ref x, ref y); Console.WriteLine(“x={0} y={1}”, x, y); } }Tham chiếu với kiểu giá trịThân của lớp Phương thứcclass SomeClass { public void HoanViChuoi(ref string s1, ref string s2) { string s = s1; s1 = s2; s2 = s; } } class Program { static void Main() { SomeClass obj = new SomeClass(); string x=“ABC”, y=“XYZ”; obj.HoanViChuoi(ref x, ref y); Console.WriteLine(“x={0} y={1}”, x, y ); } }Tham chiếu với kiểu tham chiếuThân của lớp Phương thứcPhương thức với tham số out Cú phápclass ClassName() { Method(out type variableName) { variableName = ; } }Thân của lớp Phương thứcCú pháp: sử dụng phương thức có tham số out//variableName = value; Method(out variableName);Chú ýTham số thực không nhất thiết phải được khởi tạo trước khi gọi phương thức.Giá trị của tham số thực không được chuyển đến tham số hình thức, vì vậy không được sử dụng tham số hình thức trong phương thức nếu chưa khởi tạo. Tham số hình thức trong phương thức phải được gán giá trị trước khi phương thức kết thúcThân của lớp Phương thứcclass AnotherClass { public int ID; } class SomeClass { public void Change1(out AnotherClass ref1) { ref1.ID = ref1.ID * 2; } public void Change2(out AnotherClass ref1) { ref1.ID = 4; }Thân của lớp Phương thức public void Change3(out AnotherClass ref1) { int x = ref1.ID; ref1 = new AnotherClass(); ref1.ID = x * 2; } public void Change4(out AnotherClass ref1) { ref1 = new AnotherClass(); ref1.ID = 99; } }Thân của lớp Phương thứcNhận xét: Nếu chúng taChỉ muốn truyền giá trị vào cho phương thứcTham trịChỉ muốn nhận 1 giá trị trả về từ phương thứcDùng lệnh returnChỉ muốn nhận nhiều giá trị trả về từ phương thứcTham chiếu outVừa muốn truyền giá trị vào phương thức vừa cho phép thay đổi giá trị đóTham chiếu refThân của lớp Phương thứcPhương thức với tham số paramsTham số params cho phép chúng ta tạo ra phương thức có số lượng không xác định các tham số Cú phápclass ClassName { Method(, params type[] variableName) { } }Thân của lớp Phương thứcclass SomeClass { public int TinhTong(params int[] arr) { int sum = 0; for (int i=0; i>==!=>=, =, ;Tạo một đối tượngCú pháp = new (); = new ();Đối tượng Truy cập thành viên publicCú pháp ObjectName.Method(params); ObjectName.Field;this.memberĐối tượng thisCú phápBài tậpCho lớp phân số (PhanSo) gồm 2 fields: tu va mauĐịnh nghĩa các constructor: default, parameter và copyĐịnh nghĩa các operator: +, -, *, / và so sánh >, =, AbstractMethodName();class ClassName:AbstractClassName { modifier AbstractMethodName() {} }Hiện thực lớp trừu tượngTrừu tượngLớp sealed và abstract using System; abstract class BaseClass { public abstract void MethodA(); public void MethodB() { Console.WriteLine ("This is the non abstract method”); } } class DerivedClass : BaseClass { public override void MethodA() { Console.WriteLine ("This is the abstract method overriden in derived class"); } }123Lớp sealed và abstract class AbstractDemo { public static void Main() { DerivedClass objDerived = new DerivedClass(); BaseClass objBase = objDerived; objBase.MethodA(); objDerived.MethodB(); } }Lớp sealed và abstract Lớp sealedLà một lớp không bao giờ dùng làm lớp cơ sởLớp abstract không thể được dùng như là một lớp sealedsealed class Point {      public Point(int x, int y)      {          this.x = x;          this.y = y;      }      public int x;      public int y; } Lớp staticLớp staticLớp static là lớp chỉ chứa các thành viên static, không thể tạo instance của lớp static và lớp static được nạp tự động bởi CLRĐặc điểm của lớp staticChỉ chứa thành viên staticKhông thể tạo đối tượngLà lớp sealedKhông chứa các instance constructorLớp staticstatic class CompanyInfo {      public static string GetCompanyName() { return "CompanyName"; } public static string GetCompanyAddress() { return "CompanyAddress"; } //... } Bài tậpTrong một công ty X, các nhân viên thuộc một trong 2 bộ phân và gọi là: nhân viên kinh doanh và nhân viên sản xuất. Thông tin cơ bản của nhân viên gồm: Mã nhân viên, họ tên. Cách tính lương cho nhân viên mỗi bộ phận như sau: Nhân viên kinh doanh: Ngoài mức lương cơ bản hàng tháng, nhân viên còn nhận được thêm 500.000/ hợp đồng được ký kếtNhân viên sản xuất: Lương nhân viên sản xuất tính theo số lượng sản phẩm x 1000. Nếu làm trên 3000 sản phẩm sẽ được thưởng thêm 5% lươngViết chương trình tính lương cho các nhân viên137Định nghĩa các phương thức sauclass NhanVienSanXuat:NhanVien{ private int soSanPham; public override void Nhap() { //Định nghĩa } public override void TinhLuong() { //Định nghĩa } }138Định nghĩa các phương thức sauclass NhanVienKinhDoanh:NhanVien{ private int luongCB; private int soHDDaKy; public override void TinhLuong() { //Định nghĩa } }139Cài đặt các phương thức sauclass DanhSachNhanVien{ List listNhanVien; public void Nhap() { //Định nghĩa }}Bài tập về nhàBổ sung các properties, constructors, các phương thức cần thiết khác trong các lớp trênViết phương thức Main() để chạy thử nghiệm kết quảQ&A141

Các file đính kèm theo tài liệu này:

  • pptxlap_trinh_tren_windows_chuong2_ngonngulaptrinhc_part1_0257.pptx