Thứ Năm, 27 tháng 11, 2014

Giới thiệu về Generic trong .net 2.0



Giới thiệu về Generic trong .net 2.0


Generic là một tính năng mới khá thú vị của .net 2.0. Vậy Generic là gi? sử dụng chúng như thế nào? Tại bài viết này chúng ta sẽ trả lời những câu hỏi đó. 

Type Safety
Các ngôn ngữ .net (C#, C++, VB..) đều là những ngôn ngữ được định kiểu mạnh, có nghĩa là bạn phải khai báo biến rõ ràng trước khi sử dụng.
Tuy nhiên khi ta sử dụng các kiểu tập hợp (collection) lại không hỗ trợ kiểu an toan( Type safety). Ví dụ kiểu ArrayList, chúng cho phép ta lưu trữ tất cả các object bên trong chúng.
using System;
using System.Collections;

namespace TestApp
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
            ArrayList list = new ArrayList();

            list.Add(3);
            list.Add(4);
            //list.Add(5.0);

            int total = 0;
            foreach(int val in list)
            {
                total = total + val;
            }

            Console.WriteLine( "Total is {0}", total);
        }
    }
}
Khi ta chạy chương trình trên sẽ cho ta kết quả là 7. Nhưng khi ta thêm dòng lệnh
list.Add(5.0);
Chương trình sẽ thông báo lỗi trong thời gian chạy.
Generic là gì?
 Generic cho phép chúng ta định kiểu an toàn (type safety). Chúng cho phép ta tạo ra một cấu trúc dữ liệu mà không cần phải xác định đó là kiểu dữ liệu gì. Tuy nhiên khi cấu trúc dữ liệu này được sử dụng, trình biên dịch phải đảm bảo rằng kiểu dữ liệu được sử dụng với nó là kiểu an toàn. Generic cũng tương đương vơi Template trong C++ tuy nhiên việc sử dụng Generic trong .net dễ dàng hơn nhiều so với Template.
List<int> aList = new List<int>();
aList.Add(3);
aList.Add(4);
// aList.Add(5.0);

int total = 0;
foreach(int val in aList)
{
    total = total + val;
}
Console.WriteLine("Total is {0}", total);
Ta sẽ không thể thực hiện đượng dòng lệnh aList.Add(5.0). Trình biên dịch sẽ thông báo lỗi. 
Generic Class
Dưới đây tôi xin giới thiệu cách khai báo cũng như sử dụng các lớp Generic
Kiểu tham số
Nhìn chung Generic class chỉ là một lớp mà nó tiếp nhận các tham số. Chính vì vậy mà các tham số của Generic class chỉ là những loại trừu tượng mà nó được sử dụng trong cấu trúc của một hay nhiều kiểu xác định trong thời gian chạy. Thuật ngữ này làm ta liên tưởng đến các kiểu tham số của Generic Class có thể đáp ứng như một PlaceHolders mà được thay thế bởi kiểu dữ liệu thực khi một Generic class được xây dựng.
public class Stack<T> {
    private T[] _items;
    private int _count;
    public void Push(T item) {...}
    public T Pop() {...}
}
Overload Type
public class MyType {
}
public class MyType<T> {
...
}
public class MyType<T, U> {
...
}
Thừa kế
public class MyBaseClass<U> {
    private U _parentData;
    public MyBaseClass() {}
    public MyBaseClass(U val) {
        this._parentData = val;
    }
}
public class MySubClass<T, U> : MyBaseClass<U> {
    private T _myData;
    public MySubClass() {}
    public MySubClass(T val1, U val2) : base(val2) {
         this._myData = val1;
    }
}
Rằng buộc tham số
Generic cho ta viết một lớp mà không cần xác định kiểu dữ liệu cụ thể, nhưng vẫn cho phép người sử dụng lớp đó chỉ ra kiểu dữ liệu cụ thể sẽ sử dụng. Điều này tạo ra sự linh hoạt bằng cách thay thế một số rằng buộc về kiểu mà có thể được sử dụng trong các tham số
public static T Max<T>(T op1, T op2) where T : IComparable
{
    if (op1.CompareTo(op2) < 0)
    return op1;
    return op2;
}
Tong ví dụ tôi đã chỉ ra rằng buộc mà kiểu được sử dụng cho kiểu tham số phải thực thi giao diện IComparable.
Các kiểu rằng buộc có thể sử dụng.
where T : struct
where T : class
where T : new() hàm khởi tạo không tham số
where T : class_name kiểu lớp mà tham số phải thừa kế
where T : interface_name kiểu giao diện mà tham số phải thực thi.
Cũng có thể sử dụng kết hợp nhiều rằng buộc như where T : IComparable, new().
Kết thúc
Trên đây là những giới thiệu cơ bản nhất về Generic trong .net 2.0.  Chúc bác bạn thành công.

Sử dụng DLL trong .NET



Sử dụng DLL trong .NET


Trong các chương trình viết trên nền .NET, ngoài việc sử dụng bộ thư viện được cung cấp bởi .NET Framework, đôi khi chúng ta có nhu cầu sử dụng các hàm được viết trên nền Windows, như các hàm API của Windows. Thông thường, các hàm này được cài đặt trong các thư viện liên kết động (DLL), được dịch bằng một trình dịch nào đó như MS Visual C++ hay Borland Delphi. 

Các thư viện khi phát hành thường gồm 3 tệp, tệp tiêu đề có phần mở rộng .h chứa khai báo các hàm, tệp thư viện nhập có phần mở rộng .lib và tệp thư viện liên kết động có phần mở rộng .dll.
Để sử dụng các hàm của thư viện liên kết động trong .NET, chỉ cần tệp .dll là đủ, ngoài ra còn cần tệp .h để biết nguyên mẫu các hàm, từ đó khai báo các hàm trong .NET một cách đúng đắn.
Trong bài này, tôi giới thiệu cách sử dụng các hàm viết trong một thư viện liên kết động, được dịch bằng Visual C++ trong một chương trình viết trên nền .NET. Thư viện minh hoạ trong bài học này là thư viện đã được giới thiệu trong bài Thư viện liên kết động, môn học Lập trình Windows. Đây là một thư viện đơn giản với hai hàm: hàm tính luỹ thừa và hàm tính giai thừa.
Tệp tiêu đề của thư viện được khai báo như sau:
/* MyDll.h */
#ifndef __MYDLL_H__
#define __MYDLL_H__
#ifdef MYDLL_EXPORTS
#define MYDLLEXPORT _declspec(dllexport)
#else
#define MYDLLEXPORT _declspec(dllimport)
#endifMYDLLEXPORT LONG CALLBACK Factorial(UINT);
MYDLLEXPORT DOUBLE CALLBACK Power(DOUBLE, UINT);
#endif // __MYDLL_H__
Tệp nguồn của thư viện:
/* MyDll.c */
#include
#include “MyDll.h”LONG CALLBACK Factorial(UINT n)
{
  return (n > 0)?n*Factorial(n - 1):1;
}DOUBLE CALLBACK Power(DOUBLE x, UINT n)
{
  return (n > 0)?x*Power(x, n - 1): 1;
}
Để sử dụng thư viện này trong .NET, dùng khai báo hàm với từ khoá extern và thuộc tính DllImport. Chương trình sau sẽ khai báo và sử dụng hai hàm này.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace UsingDll {
  class Program {
    [DllImport(”MyDll.dll”)]
    private static extern int Factorial(int n);    [DllImport(”MyDll.dll”)]
    private static extern double Power(double x, int n);
    static void Main(string[] args) {
      Console.WriteLine(”2.0 to power 3 is {0}”,
        Power(2.0, 3));
      Console.WriteLine(”Factorial of 5 is {0}”,
        Factorial(5));
    }
  }
}

Một trong những vấn đề thường gặp phải khi sử dụng hàm viết trên nền Windows trong .NET là kiểu dữ liệu giữa hai nền không giống nhau. Sau đây là một vài kiểu chuyển đổi để có thể gọi hàm một cách đúng đắn.
Kiểu số nguyên 1 byte:
Windows: TCHAR (char), BYTE (unsigned char).
.NET: sbyte, byte (C#), Byte (VB.NET).
Kiểu số nguyên 4 byte:
Windows: int, UINT (unsigned int), DWORD (unsigned int), LONG (long).
.NET: int (C#), Integer (VB.NET) hoặc Int32.
Kiểu số thực 4 byte:
Windows: float.
.NET: float (C#), Single (VB.NET)
Kiểu số thực 8 byte:
Windows: double.
.NET: double (C#), Double (VB.NET).
Kiểu xâu ký tự (vào):
Windows: LPCTSTR, LPSTR.
.NET: string (C#), String (VB.NET).
Kiểu xâu ký tự (ra):
Windows: LPSTR.
.NET: byte[], sbyte[] (C#), Byte [] (VB.NET)
Ghi chú:
- Các hàm trong DLL phải được dịch theo chế độ gọi stdcall. Để dịch theo chế độ stdcall, các hàm nên khai báo với chỉ thị _stdcall (WINAPI, CALLBACK) và đặt các tệp nguồn có phần mở rộng là .c. Để kiểm tra một hàm trong một DLL có sẵn có phải được gọi theo kiểu stdcall không, mở DLL và xem định dạng của hàm trong DLL phải có dạng _<Tên hàm>@<Độ dài tham số>. Ví dụ hai hàm trên sẽ có tên đầy đủ là: _Factorial@4 và _Power@12.- Trong trường hợp lấy xâu ký tự ra từ hàm, cần phải truyền vào một mảng kiểu byte (ANSI) hoặc char (Unicode) đủ lớn, sau đó ghép các ký tự nhận được trong mảng để tạo thành xâu ký tự kiểu string.




Thứ Ba, 25 tháng 11, 2014

Phân trang trong winform

 

Phân trang trong winform - Paging in winform. Hướng dẫn chi tiết + Source code

Phân trang trong web là 1 phương pháp được sử dụng rất phổ biến. Nhưng phân trang trong winform thì thế nào? Đó là ý tưởng hay mà chính mình, rất nhiều sinh viên và nhiều người lập trình không hề hay ít khi nghĩ tới. Mình xin tut bài này để cho mọi người có thể tham khảo, trao đổi. Có gì sai sót mong các bạn cứ thẳng thắn cho ý kiến.

Ý tưởng:
- Thay vì load tất cả các dòng (record) để đưa ra lưới, thì chúng ta chỉ thực hiện đếm số dòng để phân trang.
- Số trang sẽ được add vào 1 combobox.
- Thông tin của trang đó sẽ được lấy về bằng 1 câu lệnh truy vấn thông qua sự kiến combo_selectIndexChange.

Thực hiện:

Bước 1: Tạo 1 Form gồm: 1 datagridView: đưa giá trị nhận được lên, 1 numberUpDown: chỉnh số dòng muốn hiển thị trên datagridView , 1 comboBox: lưu số trang và để thực hiện chuyển trang.

Bước 2: Khai báo các biến
        int currentPageIndex = 1;
        int pageSize = 17; //Số dòng hiển thị lên lưới
        int pageNumber = 0; //Số trang
        int fistRow, lastRow; //Dòng bắt đầu, dòng kết thúc cho việc truy vấn dữ liệu
        int rows; //Số dòng được trả về từ câu truy vấn trong formLoad
//SQL connection bạn thay đổi sao cho phù hợp với CSDL của mình, Mình chỉ post code thôi :D
        SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=QLSX;Integrated Security=True");

Bước 3: Trong sự kiện formLoad
private void Form1_Load(object sender, EventArgs e)
{
     string sql = "select count(*) as MaxNumber from KyHieuSo";//Đếm số dòng để phân trang, các bạn nhớ đổi KyHieuSo thành tên bảng phù hợp nha
     SqlCommand cmd = new SqlCommand(sql, conn);
     conn.Open();
     rows = Convert.ToInt32(cmd.ExecuteScalar()); //Số dòng nhận được
     pageTotal();
     conn.Close();
 }

Bước 4: Tạo hàm PageTotal đển phân trang và add số trang vào comboBox
void pageTotal()
{
    pageNumber = rows % pageSize != 0 ? rows / pageSize + 1 : rows / pageSize;
    label3.Text = " / " + pageNumber.ToString();
    cmbPage.Items.Clear();
    for (int i = 1; i < pageNumber; i++)
    {
        cmbPage.Items.Add(i + "");
    }
    cmbPage.SelectedIndex = 0;
}

Bước 5: Khi click chọn trang thì thực hiện câu lệnh truy vấn
  private void cmbPage_SelectedIndexChanged(object sender, EventArgs e)
  {
      currentPageIndex = Convert.ToInt32(cmbPage.Text);
      fistRow = pageSize * (currentPageIndex - 1); //Dong dau
      lastRow = pageSize * (currentPageIndex);//Dong cuoi cua 1 trang duoc chon.
      //MessageBox.Show(fistRow + " " + lastRow);
      string sql = "select Row_number() over(order by KyHieu) STT, * from KyHieuSo";
      SqlDataAdapter da = new SqlDataAdapter(sql, conn);
      DataSet ds = new DataSet();
      da.Fill(ds, fistRow, pageSize, "KyHieuSo");
      dataGridView1.DataSource = ds.Tables[0];
  }

Thế là xong! Nếu bạn chưa tin, hãy bật TaskManager lên để so sánh dung lượng chiếm dụng bộ nhớ của phần mềm chưa phân trang và phần mềm đã phân trang, dung lượng của chương trình đã phân trang giảm đi rất nhiều (thời gian nhanh hơn hay không thì mình chưa test kĩ, hi)

(Chú ý: đây là chương trình có tính minh họa, vì vậy các bạn phải đổi đường dẫn CSDL...)

Thứ Hai, 24 tháng 11, 2014

Điền dấu +/- vào giữa các chữ số để có Tổng = n

Điền dấu +/- vào giữa các chữ số để có Tổng = n bằng phương pháp Quay lui?


1. Hàm insert() đưa xâu "123456789" về xâu "1*2*3*4*5*6*7*8*9" 2. Vị trí các dấu có thể
     - 
giữ nguyên dấu *: hiểu là các số được viết liền nhau1*12.
     
Thay bởi dấu +: hiểu là phép trừ1-= -1.
     
Thay bởi dấu +: hiểu là phép cộng1+2=3. 
     Việc hiểu này dành cho hàm 
eval(). 3. Hàm Try() được cài đặt đệ quy phù hợp với giải thuật quay luiNó lần lượt thử các vị trí có dấu bởi 3 khả năng trên.
 
Với mỗi phép thử của nóhàm eval() được gọi để evaluate giá trị của xâu,
 
tương ứng với biểu thức do hàm removemultsign() trả về.
 
Nếu khớp với giá trị sum mong muốn thì printf ra thôi.
 
Thực chất của quay lui được thực hiện bởi câu lệnh str[k] = chđó.  




Demo:

#include <stdio.h>
#include <stdlib.h>
#define max 100
int isdigit(char ch) {return '0' <= ch && ch <= '9';}
int isoperator(char ch) {return ch == '+' ? 1: -1;}
int ismultsign(char ch) {return ch == '*';}
long eval(char *str)
{
    long val = 0;
    char sign = '+';
    long temp = 0;
    int k = 0;
    char *ptr = str;
    while (*ptr)
        {
            if (isdigit(*ptr))
                temp = temp*10 + *ptr - '0';
            else if(!ismultsign(*ptr))
                {
                    val = val + isoperator(sign)*temp;
                    temp = 0;
                    sign = *ptr;
                }
            ptr ++;
        }
   
    return val + isoperator(sign)*temp;
}
char *insert(char *str)
{
    char *temp = (char *) malloc(2*max);
    char *ptr = str;
    int k = 0;
    while (*ptr)
        {
            k+=2;
            temp[k-2] = *ptr;
            temp[k-1] = '*';
            ptr ++;
        }
    temp[k-1] = 0;
    return temp;
}
char *removemultsign(char *str)
{
    char *temp = (char *) malloc (max);
    char *ptr = temp;
    while (*str)
        {
            if(!ismultsign(*str))
                {
                    *ptr = *str;
                    ptr++;
                }
            str++;
        }
    *ptr = 0;
    return temp;
}
void Try(char *str, int k, int sum)
{  
    if (*(str+k))
        for (int j = 0; j < 3; j++)
            {
                char ch = str[k];
                if (j==1) str[k] = '-';
                else if (j==2) str[k] = '+';
                long num = eval(str);
                if (num == sum)
                    {
                        char *temp = removemultsign(str);
                        printf("\n%s = %d\n",temp, sum);
                        free (temp);
                    }
                Try(str,k+2,sum);
                str[k] = ch;
            }
}
void main()
{
    char str[] = "123456789";
    char *ptr = insert(str);
    Try(ptr,1,12);
    free (ptr);
}

Bài đăng phổ biến