카테고리 보관물: 개발

C# – DataTable활용

C# 에서 제공하는 자료 구조가 많이 있지만 저는 DataTable을 즐겨 사용합니다.
2차원 배열과 같은 구조로 되어 있고 실제 DB Query 의 결과값을 Tool 에서 보는 것과 동일한 형태로 값을 가지고 있습니다.

DataTable을 컨트롤에 직접 바인딩할 수도 있고 배열처럼 반복하면서 출력할 수도 있어서 상황에 따라 다양하게 이용하게 됩니다.

프로젝트를 진행하면서 유용하였던 몇 가지 DataTable 활용 방법을 정리해 보았습니다.

1. DataTable의 내용을 다시 Query하여 그 결과를 DataTable로 대입하는 방법
간혹 Query 결과를 가지고 있는 DataTable을 어떤 조건으로 다시 필터링하거나 결과를 한정하여야 하는 경우가 생기게 됩니다. 물론 SQL 문이나 Procedure를 작성하여 분리할 수 있지만 번거로운 것이 사실입니다. 이때 DataTable에 람다 식을 사용하여 Query할 수 있습니다. 그 결과를 DataTable에 대입하여 다시 사용할 수 있습니다.

DataTable contactType = ContactSetting.AsEnumerable().Where(i => i.Field<String>(“contact_check_yn”) == “Y”).CopyToDataTable();

 

위의 예는 ContactSetting DataTable의 내용 중 contact_check_yn 필드의 값이 ‘Y’인 것만을 추출하여 contactType DataTable에 대입하는 구문입니다. 이런식으로 사용하여 DataTable의 내용을 별도의 DB 연결 없이 필터링하여 값을 한정할 수 있습니다.

2. DataTable의 내용 중 특정한 조건으로 필터링한 값이 있는지 판단하는 방법
DataTable 내용 중 특정한 조건의 값이 존재하는지 판단할 수 있습니다. 물론 반복문으로 특정한 필드의 값을 비교하여 판단할 수 있지만 위의 방법과 비슷하게 추출할 수 있습니다.

DataTable respondent = getRespondentDetailInfo(listNo);
var respondentEditItemRows = respondent.AsEnumerable().Where(i => i.Field<String>(“list_edit_yn”) == “Y”);if (respondentEditItemRows.Any() == false)
{
pnlRespodentEditButton.Visible = false;
}

 

위의 예는 respondent DataTable 내의 list_edit_yn 필드의 값이 ‘Y’인 행이 존재하는지 여부를 판단하는 코드 입니다. Query한 결과를 Any() 메소드로 값이 존재하는지 판단하여 그 후속 작업을 진행하는 코드 입니다.

3. DataTable내의 특정한 Row 삭제
여러가지 이유로 DataTable내의 특정한 Row를 삭제하여야 하는 경우가 있습니다. DataTable에서 값을 유일하게 구별할 수 있는 Primary key 필드를 필터링하여 삭제하는 방법입니다.

int labelSeq = Convert.ToInt32(option.columnScreenFilter.Items[i].Value);var deleteRow = columnLabelList.AsEnumerable().Where(x => x.Field<Int32>(“label_seq”) == labelSeq).Cast<DataRow>().FirstOrDefault();
columnLabelList.Rows.Remove(deleteRow);

 

1번의 예와 같이 비슷하게 columnLabelList DataTalbe의 label_seq 필드의 값이 labelSeq 변수의 값과 동일한 Row를 추출하고 그 Row를 DataTable에서 삭제하는 방법입니다.

4. DataTable을 대상으로 배열에 존재하는 값에 해당하는 Row만 가져와서 DataTable로 반환하는 방법
1번의 예와 비슷하다고 할 수 있는데 특정한 값이 아니라 배열내에 존재하는 값을 대상으로 한다는 것이 다릅니다.

string[] topBannerItemResponseValue = new string[10];for (var j = 0; j < topBannerItemResponseValue.Length; j++) { topBannerItemResponseValue[j] = Convert.ToInt32(topBannerLabelListResult.Rows[j][“response_value”]).ToString(); }

DataTable topBannerLabelListNew = topBannerLabelList.AsEnumerable().Where(x => topBannerItemResponseValue.Contains(x.Field<String>(“label_values”))).CopyToDataTable();

 

string 배열 변수 topBannerItemResponseValue에 추출하고자 하는 값을 대입합니다. topBannerLabelList DataTable의 label_values 값을 대상으로 배열변수내의 값과 같은 결과를 Query하여 topBannerLabelListNew DataTable에 대입하는 코드 입니다.

5. DataTable을 특정한 필드로 정렬
DB를 Query하여 가져온 결과를 다른 필드의 값을 기준으로 정렬하여야 하는 경우가 있습니다.

DataTable banner = getBanner();DataView dv = banner.DefaultView; dv.Sort = “sort_order”;

banner = dv.ToTable();

 

banner DataTable 내의 sort_order 필드로 정렬하여 그 결과를 다시 banner DataTable에 대입하는 코드 입니다.

DataTable을 이용하시는데 도움이 되었으면 좋겠습니다.

Delphi – 숫자야구 게임

이번에는 간단한 숫자야구를 제작해 보도록 하겠습니다.

숫자야구는 학창 시절에 친구들과 해보신 기억이 있으실 겁니다. 규칙은 간단한데 두 사람이 서로 세자리 수를 중복 없이 정하고 번갈아가며 세자리 숫자를 제시하면 상대방이 자신이 정한 숫자를 보고 Strike와 Ball을 판정해 줍니다. 숫자가 있는 경우에만 Strike와 Ball이 되고 아예 없으면 Out이 됩니다.

숫자가 있으면서 위치가 같으면 Strike, 위치가 같지 않으면 Ball 입니다. 예를 들어 보도록 하겠습니다.

A와 B가 게임을 한다고 가정하고 A는 587을 정했고 B는 698을 정했습니다. 먼저 A가 B에게 123을 제시합니다. B는 자신이 정한 숫자에 123이 들어있지 않으므로 Out이라고 알려줍니다. 이어 B는 A에게 968을 제시합니다. A는 자신의 숫자에 8이 들어 있고 위치한 곳이 세 번째 이므로 1Ball 이라고 알려줍니다. 이런 방법으로 번갈아서 숫자를 주고받으면서 예측을 하여 먼저 맞힌 사람이 승리하는 게임입니다.

이 강좌에서는 컴퓨터가 생성한 임의의 세자리 숫자를 알아맞히는 프로그램을 제작해 보도록 하겠습니다.

새 프로젝트를 시작하고 다음과 같이 폼을 디자인 합니다.

Form Design

Form Design

다음의 표를 참고하여 각 컴포넌트의 속성을 적당하게 변경하고 이름을 구분이 가능하도록 지정합니다.

컴포넌트 속성 설명
Button
 Name  : btn1
 Caption  : 1
 숫자1 버튼
Button
 Name  : btn2
 Caption  : 2
 숫자2 버튼
Button
 Name  : btn3
 Caption  : 3
 숫자3 버튼
Button
 Name  : btn4
 Caption  : 4
 숫자4 버튼
Button
 Name  : btn5
 Caption  : 5
 숫자5 버튼
Button
 Name  : btn6
 Caption  : 6
 숫자6 버튼
Button
 Name  : btn7
 Caption  : 7
 숫자7 버튼
Button
 Name  : btn8
 Caption  : 8
 숫자8 버튼
Button
 Name  : btn9
 Caption  : 9
 숫자9 버튼
Button
 Name  : btnOK
 Caption  : 확인
 확인 버튼
Button
 Name  : btnNew
 Caption  : 새게임
 새게임 버튼
Button
 Name  : btnBackSpace
 Caption  : BS
 BS 버튼
Button
 Name  : btnClear
 Caption  : C
 Clear 버튼
Label
 Name  : lblCount
 Caption  : 시도횟수
 제목 라벨
Edit
 Name  : txtNumber
 Text  :
 Readonly  : True
 숫자입력표시 Edit
Edit
 Name  : txtValue
 Text  :
 Visible  : False
 컴퓨터 생성 숫자 저장 Edit
ListBox
 Name  : lsbResult
 숫자 유추 결과 출력

메인폼의 OnActivate 이벤트에 중복이 되지 않은 세자리 숫자를 생성하도록 합니다. 생성한 숫자를 Visible 속성을 False로 설정한 Edit 컴포넌트(txtValue)의 Text 속성에 저장합니다.

procedure TfrmMain.FormActivate(Sender: TObject);
var
tempLoop : Integer;
createdNumber : Array[1..3] of Integer;
begin

txtNumber.Text := ”;
intTryCount := 0;

Randomize;

//세개의 중복되지 않은 숫자를 만들어 숨겨진 TEdit 컴포넌트에 저장
for tempLoop := 1 to 3 do
begin

//루프의 처음이면 바로 숫자 생성
if tempLoop = 1 then
begin
createdNumber[tempLoop] := RandomRange(1,9);
end
//두번째인경우 첫번째 숫자와 같지 않도록 생성된 숫자와 비교하여
//배열에 저장
else if tempLoop = 2 then
begin
createdNumber[tempLoop] := RandomRange(1,9);

while createdNumber[1] = createdNumber[2] do
begin
createdNumber[tempLoop] := RandomRange(1,9);
end

end
//세번째인경우 첫번째, 두번째 숫자와 같지 않도록 생성된 숫자와 비교하여
//배열에 저장
else if tempLoop = 3 then
begin

createdNumber[tempLoop] := RandomRange(1,9);

while (createdNumber[1] = createdNumber[3]) or (createdNumber[2] = createdNumber[3]) do
begin
createdNumber[tempLoop] := RandomRange(1,9);
end

end;

//숨겨진 TEdit 필드에 저장
txtValue.Text := IntToStr(createdNumber[1]) + IntToStr(createdNumber[2]) + IntToStr(createdNumber[3]);
end;

intFirstNumber := StrToInt(Copy(txtValue.Text, 1, 1));
intSecondNumber := StrToInt(Copy(txtValue.Text, 2, 1));
intThirdNumber := StrToInt(Copy(txtValue.Text, 3, 1));

for tempLoop := 1 to 9 do
ChangeButtonStatus(tempLoop, True);

btnOK.Enabled := True;
btnBackSpace.Enabled := True;
btnClear.Enabled := True;
lsbResult.Items.Clear;
end;

 

루프의 처음이면 일단 숫자를 하나 생성하고 배열변수에 저장합니다. 그 다음으로 첫번째 숫자와 같지 않을 때까지 생성하여 배열변수에 저장합니다. 세번째 숫자도 이전과 같은 방법으로 중복되지 않도록 생성합니다. 그리고 나서 각 숫자를 intFirstNumber, intSecondNumber, intThirdNumber에 대입합니다.

그리고 모든 숫자버튼과 결과출력을 초기화 합니다.

맞추려는 숫자가 생성되었으니 사용자로 부터 입력을 받아 그 결과를 ListBox에 출력하면 됩니다.

procedure TfrmMain.btnOKClick(Sender: TObject);
var
intUserFirstNumber, intUserSecondNumber, intUserThirdNumber : Integer;
intStrikeCount, intBallCount : Integer;
intLoop : Integer;
begin

if Length(txtNumber.Text) = 3 then
begin

intStrikeCount := 0;
intBallCount := 0;

if txtNumber.Text = txtValue.Text then //숫자를 맞춘경우
begin
Inc(intTryCount);

lsbResult.Items.Insert(0, IntToStr(intTryCount) + ‘ : ‘ + txtNumber.Text + ‘ Correct.’);
btnOK.Enabled := False;
btnBackSpace.Enabled := False;
btnClear.Enabled := False;
end
else
begin
intUserFirstNumber := StrToInt(Copy(txtNumber.Text, 1, 1));
intUserSecondNumber := StrToInt(Copy(txtNumber.Text, 2, 1));
intUserThirdNumber := StrToInt(Copy(txtNumber.Text, 3, 1));

//첫번째 숫자
if (intUserFirstNumber = intFirstNumber) then
Inc(intStrikeCount)
else
begin
if (intUserFirstNumber = intSecondNumber) then Inc(intBallCount);
if (intUserFirstNumber = intThirdNumber) then Inc(intBallCount);
end;

//두번째 숫자
if (intUserSecondNumber = intSecondNumber) then
Inc(intStrikeCount)
else
begin
if (intUserSecondNumber = intFirstNumber) then Inc(intBallCount);
if (intUserSecondNumber = intThirdNumber) then Inc(intBallCount);
end;

//세번째 숫자
if (intUserThirdNumber = intThirdNumber) then
Inc(intStrikeCount)
else
begin
if (intUserThirdNumber = intFirstNumber) then Inc(intBallCount);
if (intUserThirdNumber = intSecondNumber) then Inc(intBallCount);
end;

Inc(intTryCount);

lsbResult.Items.Insert(0, IntToStr(intTryCount) + ‘ : ‘ + txtNumber.Text + ‘ ‘ + IntToStr(intStrikeCount) + ‘ S ‘ + IntToStr(intBallCount) + ‘ B’);

for intLoop := 1 to 9 do
ChangeButtonStatus(intLoop, True);

txtNumber.Text := ”;

end;

end

end;

 

입력된 숫자를 각각 분리하여 컴퓨터가 생성한 것과 비교하여 그 결과를 출력합니다. ListBox에 Add가 아니라 Insert로 항목을 출력한 것을 마지막에 입력한 숫자가 가장 위에 나타나도록 하기 위함입니다. 판정된 결과를 시도 횟수와 함께 출력해 주어 사용자가 숫자를 추측하기 편하도록 합니다. 이 강좌에서는 이미 입력된 숫자라고 해도 다시 입력 받는데 받지 않도록 개선하면 더 완성도 높은 프로그램이 될 것입니다.

생성된 숫자를 맞추면 더 이상의 입력이 불가능 하도록 확인 버튼을 비활성화 합니다.

나머지 소스는 올려드린 자료를 참고하시면 됩니다.

이 숫자야구 게임은 간단한 프로그램이라 할 수 있는데 별 다른 생각 없이 PC앞에 앉아 바로 코딩을 진행했습니다. 그런데 숫자버튼의 처리나 비교하는 과정에서 바로 생각나는 대로만 하다 보니 나중에는 비슷한 기능을 하는 함수가 여러 개 만들어 지고 기능은 만들어져 있는데 사용되지 않는 함수도 나타나게 되었습니다. 중요한 루틴만을 제외하여 정리하여 필요한 루틴만이 들어가도록 하였습니다. 이것도 완성도가 높다고는 할 수 없습니다. 버튼을 초기화 하는 부분이 중복이 되어 있고 매끄럽지 못한 부분도 눈에 많이 들어옵니다. 한번 스스로 고쳐보시기 바랍니다.

말씀 드리고 싶은 것은 아무리 간단한 프로그램이라고 하더라도 PC앞이 아니라 책상 앞에 앉아 연습장에 생각을 정리하고 어떻게 진행할 것인가를 계획하라는 것 입니다. 물론 약간의 시간이 더 들고 그렇게 할 필요성이 없다라고 생각되시더라도 습관을 들이시는 것이 좋습니다. 저도 간단하게 생각을 하고 바로 코딩을 했다가 다시 한번 뼈저리게 느낀 사실입니다. 한번 더 생각을 정리하면 나중에 많은 시간과 노력을 줄일 수 있음을 꼭 잊지 마시기 바랍니다.

다운로드 : 소스파일