Generics merupakan type baru dalam .NET 2.0. Framework .NET 2.0 memiliki beberapa class generic dalam namespace System.Collections.Generic, di antaranya Dictionary, Queue, SortedDictionary, dan SortedList. Class-class tersebut bekerja seperti halnya class nongeneric dalam namespace System.Collections dengan penambahan kinerja dan type safety.
Kegunaan Generics.
Pada Framework .NET 1.0 dan 1.1 biasanya menggunakan class Object pada parameter dan member untuk melakukan casting dari dan ke class Object. Ada dua kelebihan generics dibandingkan dengan class Object:
- Mengurangi kesalahan saat run-time. Compiler tidak dapat mendeteksi kesalahan jika terdapat casting dari dan ke class Object. Sebagai contoh, jika terdapat castring dari string ke class Object dan dilakukan casting dari class Object tersebut ke integer, compiler tidak dapat mendeteksi kesalahan tersebut. Pada saat program dijalankan, runtime .NET melempar exception karena terdapat kesalahan casting dari string ke integer. Dengan menggunakan generic, compiler dapat memberitahukan kesalahan ini sebelum program dijalankan. Dengan generic juga dapat ditentukan constraint untuk membatasi class-class yang digunakan pada generic, sehingga compiler dapat mendeteksi type yang tidak cocok.
- Perbaikan Kinerja. Casting membutuhkan boxing (konversi dari value type ke reference type) dan unboxing (konversi dari reference type ke value type) yang membutuhkan sumber daya prosesor lebih banyak dan kinerja program lebih lambat. Dengan menggunakan generic tidak diperlukan casting atau boxing sehingga menambah kinerja run-time.
Untuk memahami perbedaan antara penggunaan class Object dan generic, lihat contoh program berikut:
class Obj
{
public Object t;
public Object u;
public Obj(Object _t, Object _u)
{
t = _t;
u = _u;
}
}
class Gen<T, U>
{
public T t;
public U u;
public Get(T _t, U _u)
{
t = _t;
u = _u;
}
}
Seperti terlihat pada program di atas, class Obj memiliki dua member dengan type Object. Class Gen memiliki dua member dengan dua member dengan type T dan U. Pada class Obj, padameter dapat diisi oleh sembarang type sedangkan pada class Gen variable sudah ditentukan typenya sehingga meminimalkan kesalahan run-time.
Penggunaan Type Generic
Ketika menggunakan Type Generic, anda harus menentukan type yang digunakan. Berikut contoh penggunaan class Obj dan Gen.
// Menambahkan dua string menggunakan class Obj
Obj oa = new Obj("Hello, ", "World!");
Console.WriteLine((string)oa.t + (string)oa.u);
// Menambahkan dua string menggunakan class Gen
Gen<string, string> ga = new Gen<string, string>("Hello, ", "World!");
Console.WriteLine(ga.t + ga.u);
// Menambahkan double dan int menggunakan class Obj
Obj ob = new Obj(10.125, 2005);
Console.WriteLine((double)ob.t + (int)ob.u);
// Menambahkan double dan int menggunakan class Gen
Gen<double, int> gb = Gen<double, int>(10.125, 2005);
Console.WriteLine(gb.t + gb.u);
Ketika program dijalankan pada aplikasi console, class Obj dan Gen menghasilkan keluaran yang sama persis. Akan tetapi kode yang menggunakan class Gen akan berjalan lebih cepat dibandingkan dengan class Obj karena tidak memerlukan boxing dan unboxing ke dan dari class Object.
Contoh program berikut menunjukkan bagaimana compiler memeriksa kesalahan sebelum program dijalankan.
// Menambahkan double dan int menggunakan class Gen
Gen<double, int> gc = Gen<double, int>(10.125, 2005);
Console.WriteLine(gc.t + gc.u);
// Menambahkan double dan int menggunakan class Obj
Obj oc = new Obj(10.125, 2005);
Console.WriteLine((int)oc.t + (int)oc.u);
Baris terakhir pada kode diatas mengandung kesalahan yaitu nilai oc.t di-casting ke int padahal seharusnya double. Kesalahan tersebut baru diketahui setelah program dijalankan, compiler tidak memberitahukan adanya kesalahan ini pada waktu program di-compile. Dengan menggunakan generic, tentunya lebih memudahkan untuk mendeteksi dan memperbaiki kesalahan sejak dini.
Menggunakan Constraint
Constraint digunakan untuk membatasi penggunaan parameter dengan type tertentu. Generic mendukung empat jenis constraint yaitu:
- Interface. Hanya mengijinkan type yang menerapkan interface tertentu untuk menggunakan generic.
- Base class. Hanya mengijinkan tyoe yang cocok atau diturunkan dari base class tertentu untuk menggunakan generic.
- Constructor. Dibutuhkan type tertentu untuk menggunakan generic untuk menerapkan constructor tanpa parameter.
- Reference atau value type. Dibutuhkan type tertentu untuk menggunakan generic salah satu dari reference atau value type.
Pada C# digunakan klausa where untuk menerapkan constraint pada generic. Sebagai contoh, class generic berikut harus digunakan hanya oleh type yang menerapkan interface IComparable.
class ComGen<T>
where T : IComparable
{
public T t1;
public T t2;
public ComGen(T _t1, T _t2)
{
t1 = _t1;
t2 = _t2;
}
public T Max()
{
if (t2.CompareTo(t1) < 0)
return t1;
else
return t1;
}
}
Class di atas akan di-compile tanpa kesalahan. Namun jika klausa where dihapus, compiler akan melaporkan kesalahan yang menunjukan bahwa tyoe T tidak mengandung definisi CompareTo. Dengan memberikan constraint generic kepada class yang mengimplementasikan IComparable, class tersebut dijamin selalu mengandung method CompareTo.
Contoh penggunaan generic pada class library Framework .NET 2.0 adalah pada List (System.Collections.Generic.List). Berikut contoh potongan kode yang menggunakan List.
class ListTest
{
public List<Employe> GetEmployes()
{
List<Employe> list = new List<Employe>();
Configuration conf = WebConfigurationManager.OpenWebConfiguration();
ConnectionStringSettings setting = conf.ConnectionStrings.ConnectionStrings["MyConn"];
SqlConnection conn = new SqlConnection(setting.ConnectionString);
SqlCommand cmd = new SqlCommand("SELECT FirstName, LastName, Age FROM Employe");
cmd.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read()) {
Employe emp = new Employe(reader["FirstName"].ToString(), reader["LastName"].ToString(), Convert.ToInt32(reader["Age"]));
list.Add(prs);
}
reader.Close();
conn.Close();
return list;
}
}
class Employe
{
private string firstname;
private string lastname;
private int age;
public string FirstName
{
get { return firstname; }
set { firstname = value; }
}
public string LastName
{
get { return lastname; }
set { lastname = value; }
}
public string Age
{
get { return age; }
set { age= value; }
}
public Age(string _firstname, string _lastname, int _age)
{
firstname = _firstname;
lastname = _lastname;
age = _age;
}
}
Contoh program di atas memanfaatkan List (dalam namespace System.Collections.Generic) untuk menampung data Employe ke dalam collection. Class ListTest menggunakan List<Employe> sebagai nilai kembalian untuk method GetEmployes. Untuk mengakses kembalian dari method GetEmployes tidak diperlukan casting melainkan tinggal dipanggil secara langsung.
// Mengakses method GetEmployes
ListTest lsTest = new ListTest();
List<Employe> list = lsTest.GetEmployes();
for (int i=0; i < list.Count; i++)
{
Console.WriteLine("Employe #{0}\n", i);
Console.WriteLine("First Name : {0}\n", list[i].FirstName);
Console.WriteLine("Last Name : {0}\n", list[i].LastName);
Console.WriteLine("Age : {0}\n", list[i].Age);
}
Dari contoh program di atas dapat dilihat bahwa tidak ada casting sehingga program dapat dibaca dengan mudah, kesalahan ketidakcocokan type dapat terdeteksi pada saat program di-compile serta kinerja program dapat meningkat.