Thứ Hai, 27 tháng 5, 2013

So sánh cú pháp của lập trình C# và Java



1.Các kiểu nguyên gốc (primitive) và kiểu đơn giản (simple)

Java có một vài kiểu primitive mà mọi người rất thân thuộc: byte, char, int, long, float, double. Những kiểuprimitive là những khối được xây dựng cơ bản của Java, chúng là những “đơn vị” nhỏ nhất. Những gì thường gây khó chịu đối với hầu hết lập trình viên là kiểu primitive thường tách rời khỏi mô hình đối tượng của Java; trong khi tất cả các đối tượng trong Java đều kế thừa từ java.lang.Object, các kiểu primitive không kế thừa từ bất kỳ gì cả. Điều này có nghĩa là bất kỳ một lớp nào khi tính toán trên các đối tượng (ví dụ như các đối tượng trong tập hợp API) sẽ không làm việc với các kiểu primitive. Các kiểu primitive sẽ phải được ánh xạ (map) thành mô hình đối tượng theo quy định để có thể sử dụng chúng.

Không có những trường hợp như thế trong C#. C# sử dụng hệ thống kiểu/đối tượng trong .NET mà ở đó, các chương trình C# có thể giao tiếp với nhiều ngôn ngữ khác trong .NET mà không gặp rắc rối nào về kiểu. Ví dụ, kiểu int là một bí danh của System.Int32 được kế thừa cuối cùng từ System.Object. Điều này có nghĩa là các kiểuprimitive, hay kiểu simple trong hàm C# cũng giống như bất kỳ các đối tượng khác. Ví dụ, điều này là đúng khi gọi phương thức toString hoặc GetType trong bất kỳ một kiểu primitive nào.

Bảng 1:Các kiểu primitive trong Java và C#
Một sự ánh xạ từ những kiểu primitive quen thuộc trong Java sang những kiểu simple tương đương trong C# (và những đối tượng mà chúng thật sự ánh xạ từ đó)
Java
C#
boolean
bool (System.Boolean)
byte
sbyte (System.Sbyte)
char
char (System.Char)
int
int (System.Int32)
long
long (System.Int64)
float
float (System.Single)
double
double (System.Double)

Mặc dù các kiểu simple trong C# là những đối tượng, tuy nhiên chúng vẫn được truyền theo tham trị (pass-by-value) tương tự như trong Java. Đây là trường hợp khác, bởi vì ngoài việc là những đối tượng, tất cả các kiểusimple trong C# đều là các đối tượng – cấu trúc (struct) khi được truyền theo tham trị sẽ được truyền theo tham biến một lần nữa (chúng ta sẽ đề cập đến phần này sau).

2.Các phát biểu (statements)
Bây giờ chúng ta có những cấu trúc dữ liệu primitive, chúng ta nên tổ chức chúng ở những phát biểu ở cấp độ cao hơn. Bạn sẽ để ý rằng, không cần phải nói nhiều với các lập trình viên Java bởi họ đã rất thân thuộc với các cú pháp này, chúng ta sẽ chỉ tập trung vào sự khác nhau giữa C# và Java

3.Khai báo (declarations)
Các biến được định nghĩa trong C# cũng giống như trong Java


PHP Code:

int integer = 3;
bool boolean = true;


Java sử dụng các từ khóa “static final” để tạo nên các biến hằng; trong Java một biến “static final” là một biến lớp thay vì là một biến đối tượng, và trình biên dịch sẽ ngăn ngừa bất kỳ các đối tượng khác thay đổi giá trị của biến. C#, theo quy định, có hai cách công bố một biến hằng.
Đánh dấu một biến bằng từ khóa const sẽ làm cho giá trị được chuyển đổi trước khi biên dịch. Với định nghĩa sau:

PHP Code:

const int two = 2;


phát biểu

PHP Code:

2 * two


được chuyển thành

PHP Code:

2 * 2


bằng vi xử lý trước khi biên dịch. Điều này sẽ làm cho chương trình đã được biên dịch sẽ chạy nhanh hơn bởi nó không phải tìm kiếm giá trị của hằng trong suốt thời gian chạy (run-time). 
Xem rằng các hằng thường được sử dụng cho BUFFERSIZE hoặc TIMEOUT, điều này có thể sẽ không gây ra một sự chuyển đổi bên trong đoạn mã; nếu một field được đánh dấu là const, khi đó bất kỳ một đoạn mã nào biên dịch nó một lần nữa sẽ không chuyển đổi hằng và sẽ cần được biên dịch lại theo quy định để thay đổi nó. Thay vì thế, nếu một hằng được đánh dấu là readonly, khi đó ứng dụng được thực thi vào lần tới, trạng thái sẽ thay đổi cũng như đoạn mã sẽ được kiểm tra giá trị của field readonly, trong khi trình biên dịch vẫn bảo vệ nó không thay đổi theo chương trình.

4.Cấu trúc điều kiện (Conditionals structure)
Có hai cấu trúc điều kiện mà các lập trình viên Java mong đợi, các phát biểu “if–then–else” và “switch”, cả hai đều có sẵn trong C#. Cả hai đều tương tự ngoại trừ một chút khác nhau trong cú pháp phát biểu “switch” của C#.

Java cho phép dòng điều khiển phải rơi vào chính xác trong các trường hợp khác nhau của phát biểu switch, trong khi trình biên dịch C# tuyệt đối không cho phép điều này – trình biên dịch C# sẽ đánh dấu đây là một lỗi cú pháp. Ví dụ, trong Java những dòng dưới đây là đúng

PHP Code:

int switching = 3;
switch(switching) {
case 3:
System.out.println(“here”);
default:
System.out.println(“and here”);
}


Kết quả là cả hai dòng lệnh println đều được thực thi. Trong C#, trong định nghĩa dòng điều khiển, nhất thiết phải có một khai báo break hoặc goto case cho những trường hợp khác nhau ở trong mỗi case.

5.Các vòng lặp (Loops)
C# cũng có những vòng lặp quen thuộc “while’, “do–while” và “for”. Ngoài ba vòng lặp này, C# còn giới thiệu một vòng lặp “foreach” để tính toán trên các đối tượng nhằm bổ sung giao tiếp System.Collections.Ienumerable. Đặc biệt hơn, các mảng trong C# (System.Array) bổ sung Ienumerable, vì thế đoạn mã Java dưới đây

PHP Code:

int[ array = // …
for( int count = 0; count < array.length; count ++ ) {
System.out.println( array[count] );
}


là đúng trong C#, nhưng nó cũng có thể được thay thế trong C# như sau:

PHP Code:

foreach( int member in array )
Console.Writeln( member );


Đặc biệt, giao tiếp Ienumerable cung cấp khả năng nhận được một sự thay thế Ienumerator cho một đối tượng (giống như java.util.Enumeration). Bất kỳ cái gì bổ sung các giao tiếp Ienumerable và Ienumerator đều có thể được tính toán trên vòng lặp foreach.

6.Các phát biểu nhảy (Jumps)
Hầu hết các phát biểu nhảy trong Java đều ánh xạ trong C#: continuebreakgotoreturn. Các phát biểu này đều sử dụng giống như cách mà chúng được sử dụng trong Java: thoát khỏi các vòng lặp hoặc trả dòng điều khiển cho một khối lệnh khác.
Những gì quan trọng để thảo luận chính là mô hình ngoại lệ (exception model), cũng như nó khác nhau cả về ngữ nghĩa và cú pháp. Tất cả các ngoại lệ trong C# đều là các ngoại lệ run-time, trình biên dịch sẽ không giúp đỡ các lập trình viên giữ lại trạng thái của các ngoại lệ. Cũng nên chú ý rằng bản thân phương thức không chứa các ngoại lệ được ném ra bên trong các khối lệnh. Cảnh báo của tôi đối với các lập trình viên Java chuyển sang C# là phải rất cẩn thận.

Khối lệnh “try-catch-finally” mà các lập trình viên Java quen thuộc vẫn tồn tại trong C#. Tất cả các ngoại lệ đều bắt nguồn từ System.Exception, vì thế việc bắt System.Exception sẽ bắt tất cả các ngoại lệ có khả năng được ném ra từ một khối lệnh, cũng giống như trong Java. Có sẵn một cách đi tắt nếu đối tượng ngoại lệ không cần thiết bằng việc sử dụng framework dưới đây

Nhưng nếu bạn cần bắt một ngoại lệ cụ thể (trong khi không yêu cầu đối tượng ngoại lệ), có thể dùng tương tự như sau:

PHP Code:

try {
}
catch (IOException) {
}


7.Các phương thức (methods)
Ở mức độ cơ bản, không có sự khác nhau giữa các phương thức trong Java và C#, mỗi phương thức đều đặt vào các tham số và có kiểu trả về. Tuy nhiên, C# có những điều chúng ta có thể làm với các phương thức mà chúng ta không thể làm với Java.
a.params

Khi gọi một phương thức, trình biên dịch Java sẽ kiểm tra xem có phương thức nào giống với phương thức yêu cầu hay không. Ví dụ trong Java, nếu có một phương thức được định nghĩa

PHP Code:

public void methodCaller ( int a, int b, int c );


thì khi có một phương thức gọi phương thức đó

PHP Code:

methodCaller( 3, 4, 5, 6 );


sẽ tạo ra một lỗi biên dịch do trình biên dịch Java không tìm thấy được bất kỳ một phương thức methodCaller()nào đặt vào 4 tham số kiểu int. Chúng ta không thể tạo ra một phương thức yêu cầu rằng cho phép chúng ta đặt vào một số lượng biến kiểu int như là các tham số.

Tại sao lại không nên có điều này? Nếu một lập trình viên muốn đặt vào một số lượng biến kiểu int, anh ta có thể định nghĩa một phương thức yêu cầu đặt vào một dãy các số nguyên và sau đó khởi dựng dãy này khi gọi phương thức. Trong Java, sẽ có tối thiểu 3 dòng lệnh chỉ để gọi phương thức – dòng đầu tiên tạo ra một dãy, một hoặc nhiều dòng sau gán các giá trị vào cho dãy, dòng thứ ba gọi phương thức. C# thay đổi điều này bằng cách cho phép các lập trình viên đặt vào một tham số trên tham số cuối cùng của dãy của phương thức, do đó một số lượng các tham số có thể đặt vào đó. Sử dụng ví dụ dưới đây, phương thức có thể được định nghĩa như sau:

PHP Code:

public void methodCaller ( params int[ a );


Và phương thức có thể được gọi bất kỳ

Bên trong phương thức, các tham số có thể được truy cập thông qua dãy “a” đã được định nghĩa.

b.ref và out

C# cũng có thể thay đổi một tham số được đặt vào như một tham biến trái với việc đặt vào với một tham trị. Ví dụ với một kiểu primitive

PHP Code:

public void increment( int a ) {
a ++;
}
int a = 3;
increment ( a );
// a vẫn bằng 3


sẽ không làm cho biến được đặt vào phương thức increment() không thật sự được tăng lên bởi vì a được đặt vào như một tham trị, biến a trong khối lệnh của phương thức increment() nằm trong môt môi trường khác và vì thế việc thay đổi trong tầm vực (scope) đó không bị ảnh hưởng đến.

Tuy nhiên thay đổi phương thức thành

PHP Code:

public void increment ( ref int a ) {
a ++;
}
int a = 3;
increment ( a );
// bây giờ thì a đã tăng lên 4


sẽ làm cho int a được đặt vào như một tham biến (giống như khi đặt vào một lớp) và vì thế việc chỉnh sửa nó sẽ làm cho giá trị gốc bị thay đổi. Sử dụng ref với một lớp cho phép một hàm gán lại con trỏ lớp bên trong hàm và thay đổi được bên ngoài phương thức.

Một từ khóa đi cặp với ref là out. Trong khi ref không bảo đảm rằng bất kỳ phép toán nào thật sự được tính toán trên biến, sử dụng out sẽ làm cho trình biên dịch chắc chắn rằng một giá trị đã được ấn định cho biến. Ví dụ đoạn mã dưới đây

PHP Code:

public void doNoThing ( out int a ) {
}


sẽ làm cho trình biên dịch hiểu rằng a sẽ không được gán cho bất kỳ gì cả.

8. Các thuộc tính (properties)
Các thuộc tính là các khởi dựng của C# thường được dùng với mô hình (patterngetter/setter trong nhiều lớp của Java. Java có một phương thức set đặt vào một tham số và phương thức get nhận về những gì tham số đã được đặt vào trước đó.

PHP Code:

private int property;
public int getProperty () {
return this.property;
}
public void setProperty ( int property ) {
this.property = property;
}


Có thể được viết trong C# như sau:

PHP Code:

private int property;
public int Property () {
get {
return this.property;
}
set {
// value là một biến được tạo ra bởi trình biên dịch để thay thế các tham số
this.property = value;
}
}


Có thể dễ dàng sử dụng bên trong một chương trình C#

PHP Code:

int currentValue = Property;
Property = new Value;


Đằng sau ngữ cảnh này, C# thật sự biên dịch property thành hai phương thức trong framework ngôn ngữ trực tiếp .NET (Intermediate Language) có tên là get_Property và set_Property. Các phương thức này không thể gọi trực tiếp từ C#, nhưng những ngôn ngữ khác sử dụng MSIL có thể truy cập các getters/setters này.

9.Từ chỉ định truy cập (Accessbility Modifiers)
Access modifier làm những gì mà tên chúng đã thể hiện – chúng giới hạn khả năng thay đổi một vùng của đoạn mã. Các modifier mà chúng ta sử dụng là privateprotecteddefaultpublic

C# lại có 5 modifier:

public – cũng giống như trong Java. Bạn có thể nhận được những gì bên trong đối tượng, bất cứ gì đều có thể truy cập tự do đến thành viên này.
protected – cũng giống như trong Java. Việc truy cập chỉ dành cho những lớp kế thừa lớp chứa từ khóa này.
internal – đây là một từ mới với những lập trình viên Java. Tất cả những đối tượng bạn định nghĩa bên trong một file .cs (bạn có thể định nghĩa nhiều hơn một đối tượng bên trong file .cs, không giống như trong Java bạn thường định nghĩa chỉ một đối tượng) có một bộ xử lý cho các thành viên bên trong.
protected internal – từ khóa này xem như là một sự kết hợp giữa protected và internal. Thành phần này có thể được truy cập từ assembly hoặc bên trong những đối tượng kế thừa từ lớp này.
private – cũng giống như trong Java. Không có bất kỳ gì có thể truy cập vào lớp ngoại trừ bên trong lớp

Các modifier này có thể được áp dụng cho cùng các cấu trúc mà Java cho phép bạn sử dụng chúng. Bạn có thể thay đổi khả năng truy cập đến các đối tượng, các phương thức và các biến. Chúng ta sẽ nói về chúng ngay dưới đây, và chúng ta có thể nói về các đối tượng và kế thừa ở phần tiếp theo.

10.Các đối tượng, các lớp và các cấu trúc
Tất cả các lập trình viên Java đều đã thân thuộc với các khái niệm về lớp, đối tượng, kế thừa. Vì thế việc học những phần tương tự trong C# chỉ là đề cập đến sự khác nhau của ngữ nghĩa. Định nghĩa một lớp như dưới đây

PHP Code:

class A {
}


nghĩa là lớp B kế thừa từ lớp A.

Các giao tiếp (interfaces) được bổ sung theo cùng cách như vậy (mặc dù có thể hơi rắc rối với các lập trình viên Java) với giao tiếp C được bổ sung cho lớp D

PHP Code:

class D : C {
}


và D có thể kế thừa B và bổ sung C

PHP Code:

class D : B, C {
}


Chú ý rằng, B được đặt trước C trong danh sách, và bất kỳ một giao tiếp nào khác được bổ sung sẽ được thêm vào sau danh sách được chia bởi dấu phẩy.

Tất cả các lớp sẽ được truyền theo tham biến cho các phương thức gọi. Điều này có nghĩa là biến được định nghĩa và được truyền thật sự là một tham biến cho vùng nhớ chứa đối tượng thật sự. Mọi thứ trong Java, ngoại trừ kiểu primitive, đều được truyền theo tham biến – không có cách nào để định nghĩa mọi thứ để có thể truyền theo tham trị. Để định nghĩa một đối tượng nhằm truyền theo tham trị trong C#, cấu trúc mang tên “struct” được sử dụng

PHP Code:

struct E {
}


struct trông giống như lớp và cũng hoạt động giống như lớp (tất cả chúng đều được bắt nguồn từSystem.Object)
struct được truyền theo tham trị thay vì theo tham biến
struct không thể kế thừa, tuy nhiên chúng có thể bổ sung các giao tiếp
struct không thể được định nghĩa một khởi dựng (contructor) mà không có tham số
struct định nghĩa các contructor với các tham số phải định nghĩa chính xác tất cả các field bởi vì nó sẽ trả về điều khiển cho phương thức nào gọi nó

Sử dụng một struct theo kiểu tự tạo thì có một tiện lợi là tiện dụng hơn trong việc xác định. Khi xác định các dãy của các lớp, trước hết bạn phải định nghĩa một dãy các reference. Sau đó, chương trình cần tương tác thông qua dãy này để tạo ra các thực thể của từng lớp. Việc chỉ xác định một dãy của các struct sẽ định vị mỗi struct khác nhau trong một block liên tiếp nhau. Như đã được đề cập ở trên. Mỗi kiểu đơn giản trong C# được định nghĩa là một struct để cung cấp cú pháp chuẩn pass-by-value mà các lập trình viên Java thường sử dụng.

11.this và base

Các đối tượng trong C# có thể tham khảo đến chính nó như trong Java. This mang cùng một nghĩa như thế nhưng C# sử dụng từ khóa base thay vì sử dụng từ khóa super như trong Java. Cả từ khóa this và base đều có thể sử dụng trong các phương thức và các contructor như this và super được sử dụng trong Java.

12.Cài chồng phương thức (Overriding methods)
Tất cả phương thức trong một đối tượng là “final” (một từ khóa trong Java) theo mặc định. Theo đó một phương thức được tải chồng bởi một đối tượng kế thừa, phương thức gốc cần phải được đánh dấu là “virtual” và tất cả các phương thức tải chồng phương thức virtual trong phương thức được kế thừa phải được đánh dấu “override

PHP Code:

public class A : Object {
public virtual void toOverride() { }
}
public class B : A {
public override void toOverride() { }
}


13.Chuyển đổi kiểu
Các lập trình viên Java thường chỉ thân thuộc với việc chuyển kiểu giữa các kiểu primitive và khi ép kiểu lên cao hơn cho siêu lớp và thấp hơn cho các lớp con. C# cho phép khả năng định nghĩa chuyển đổi kiểu tự tạo cho hai đối tượng bất kỳ. Hai kiểu chuyển đổi phải như sau:

Chuyển đổi tương đối: kiểu chuyển này yêu cầu kiểu đích phải được xác định trong phát biểu,cũng như việc chuyển đổi này không chắc chắn làm việc hoặc nếu nó làm việc thì kết quả của nó có thể bị mất đi thông tin. Các lập trình viên Java thường thân thuộc với việc chuyển đổi tuyệt đối khi ép một đối tượng thành một một đối tượng của các lớp con của nó.
Chuyển đổi tuyệt đối: việc chuyển đổi này không yêu cầu kiểu cha, cũng như việc chuyển đổi này chắc chắn làm việc.
Dưới đây, chúng ta chuyển một kiểu double thành một kiểu FlooredDouble và ngược lại. Chúng ta cần định nghĩa việc chuyển kiểu tương đối từ double thành FlooredDouble, thông tin có thể bị mất đi trong quá trình chuyển đổi. Ở quá trình ngược lại, chúng ta sẽ định nghĩa việc chuyển kiểu tuyệt đối, và sẽ không bị mất thông tin.

PHP Code:

public class FloorDouble : Object{
private double value;
public FloorDouble( double value ) {
this.value = Math.Floor( value );
}
public double Value {
get {
return this.value;
}
}
public static explicit operator FloorDouble( double value ) {
return new FloorDouble( value );
}
public static implicit operator double( FloorDouble value ) {
return value.Value;
}
}
// this class can be used by
FloorDouble fl = (FloorDouble)10.5
double d = fl;


14.Tải chồng toán tử (Operator overloading)
Tải chồng toán tử trong C# rất đơn giản. Lớp FlooredDouble ở trên có thể được thừa kế để chứa một phương thức static


PHP Code:

public static FloorDouble operator + ( FloorDouble fd1, FloorDouble fd2 ) {
return new FloorDouble( fd1.Value + fd2.Value );
}


Và các phát biểu sau là đúng

PHP Code:

FloorDouble fd1 = new FloorDouble( 3.5 );
FloorDouble fd2 = new FloorDouble( 4 );
FloorDouble sum = fd1 + fd2;


15.Tổ chức lại mã nguồn

C# không đặt bất kỳ yêu cầu nào trong việc tổ chức file – một lập trình viên có thể sắp xếp toàn bộ chương trình C# bên trong một file .cs (Java thường yêu cầu một file .java chứa một lớp).
C# cũng cung cấp một cách để chia nhỏ các đối tượng của chương trình tương tự như các khối trong Java. Sử dụng namespace, các kiểu có quan hệ có thể được nhóm vào trong một phân cấp. Nếu không có block “namespace” nó sẽ nằm trong một namespace mặc định.

PHP Code:

namespace com.oreilly {
}


Và tất cả các kiểu được định nghĩa trong block tồn tại trong phân cấp com.oreilly. Các phân cấp này có thể được lồng vào nhau

PHP Code:

namespace com {
namespace oreilly {
}
}


cho phép đoạn mã có thể được viết với những namespace lồng nhau.
Để bổ sung một namespace, phải dùng từ khóa “using”. Các hàm cơ bản này cũng tương tự như phát biểu “import” trong Java, tuy nhiên dùng để bổ sung một lớp xác định. Using cũng có thể được dùng để ánh xạ các kiểu và các namespace. Ví dụ định nghĩa sau:

PHP Code:

namespace a.really.long.namespace {
class theClass {
}
}


có thể ánh xạ như sau:

PHP Code:

using short = a.really.long.namespace.theClass;


16.Tổng kết
Trong article này, không đề cập toàn bộ cú pháp của C# như mã không an toàn, xử lý lại… và các phát biểu khác. Thay vào đó, chúng ta nói đến một danh sách các phát biểu thân thuộc và tương ứng với những gì trong Java mà thôi.

0 nhận xét:

Đăng nhận xét