블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2794)
Unity3D (852)
Programming (478)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (11)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
출처 : http://www.gamecode.org/tt/entry/1851

1. 도입

게임을 만드는 데 있어서 높은 프레임 레이트는 모든 프로그래머들의 공통적인 관심사가 아닐까 싶다. 이러한 관심사에 대한 아이디어와 이미 많이 적용되고 있는 것들을 정리하고자 한다.
(사실 제 관심사는 이런 거보단 '어떻게 해야 빨리 개발할 수 있나'입니다만, 항상 실패만 해서... T_T;;)

2. 하드웨어 가속

하드웨어의 발전과 함께 최적화에 대한 접근도 시시각각 변하고 있는 데, 최근 가장 큰 화제는 3차원 가속기를 얼마나 활용하느냐 하는 문제이다.

비디오카드가 최적화된 아키텍처로 렌더링의 많은 부분을 처리해 주면서 얻은 이점 중에 두 가지에 주목할 필요가 있다.

첫 번째로 기존의 CPU에서 해주어야 했던 일거리를 더욱 빠르고 효율적으로 처리해준다는 점이다. 즉 프로젝션, 클리핑등의 일을 CPU보다 GPU에서 더욱 빠르게 수행한다는 얘기다. (GPU가 CPU보다 코어 스피드는 느릴 수도 있지만, 최적화된 메모리 억세스, 다수의 계산 유닛을 통한 병렬적인 계산등을 통해 더 많은, 즉 더 빠른 계산을 하도록 설계되기 때문이다.) 얼마나 많은 일을 CPU가 아닌 GPU가 하느냐가 최적화의 포인트라고 할 수 있다.

두 번째로 병렬성에 있다. 즉, 비디오카드의 GPU와 메인CPU와는 완전히 별개로 수행된다는 점이다. 즉, 비디오카드가 열심히 렌더링하는 시점에도 CPU는 다른 일을 할 수 있다는 점이다. 즉, 비디오카드가 렌더링 할 동안 CPU가 더욱 많은 일을 하는 것이 최적화의 포인트겠고, 비디오카드가 한순간도 쉬지 않고 렌더링 작업을 하는 것이 가장 이상적이라고 할 수 있다. (여담으로 XBOX 개발킷 중에 카르마란 프로파일링 도구를 본 적이 있는 데, 그 툴의 기능은 원하는 타이밍의 프레임에 비디오카드가 idle 이 되는 구간을 - 즉 CPU가 일을 못 주고 혼자 뭔가 연산하는 타이밍 구간- 측정해서 리포트해주는 것이다. 이러한 관점에서 프로그래밍 허점을 찾도록 도와주는 도구인데 하드웨어 성능을 살리는 데 상당히 도움이 될 꺼란 생각이 든다.)

어떻게 해야 더욱 빠르게 렌더링 되느냐 같은 문제는 다음 글에서 다루기로 하고, 더 이상 렌더링 속도를 개선할 수 없는 상황이라고 가정하면, 위의 두 가지가 그것을 극복할 수 있는 방법이 될 것이다.

첫 번째의 경우에는 사실상 최적화할 수 있는 부분이 정해져 있기 때문에 [1], 하드웨어의 T&L과정을 완전히 GPU에 넘긴 후라면, 연산을 GPU로 넘긴다는 관점에서는 더 이상 해줄 수 있는 것이 없다.

하지만, 두 번째의 경우에는 특별한 무언가만 해준다면 쉽게 큰 효과를 얻을 수도 있는 데, 차근 차근 생각해보도록 하자.

3. 일반적인 CPU 와 GPU 의 작업 타이밍

메인 루핑은 렌더링과 전혀 상관없는 _process() 와 렌더링만 하는 _display() 함수로 구분해보자. (만약 인공지능 루틴 등에 렌더링 루틴이 삽입되어 있다면 원하는 대로 렌더링 배치를 하기가 힘드므로, 여기서 얘기하는 것을 적용하기는 쉽지 않다.)

만약 이와 같은 구성이라면 일반적인 프로세스의 처리 그래프는 [그림3.1]과 같을 것이다.


(그림 3.1)

간단히 그림을 살펴보면 먼저 process 가 수행되는 동안에는 GPU가 놀고 있다. 그러다. DISPLAY 가 시작되면 본격적으로 GPU 가 활동하게 된다. DISPLAY 루틴 상 부하가 있다면 위처럼 비디오 카드가 놀고 있는 타이밍이 있을 수 있다. 그리고 DISPLAY 루틴이 끝나는 지점 -일반적으로 flip 을 하거나 present 를 하는 순간- 부터 CPU는 비디오카드의 내부 큐(리스트)에 있는 작업들이 끝날 때까지 대기를 한다. (파란색 선 사이가 한 프레임을 나타낸다.)

[그림3.1]의 모양은 나타내는 프로그램은 비효율적으로 프로그래밍을 한 것이다. 하나씩 체크해보면서 이 구성을 최적화해서 비디오카드가 전혀 쉬지 않고 작동할 수 있도록 구성해본다.

4. 렌더링 루틴과 타이밍의 관계

먼저, GPU의 타이밍에 대해서 확인하고 넘어가자.


(그림 4.1)

[그림4.1]과 같은 경우를 생각해본다. CPU에서 매트릭스 트랜스폼하는 시간을 1 이라고 표시했다. A라는 오브젝트를 DrawPrimitive 하게 되면 거의 딜레이 없이 CPU에게 프로세스가 넘어오며, 그와  동시에 CPU와는 별개로 GPU는 렌더링을 한다. 여기서 CPU는 다시 매트릭스 트랜스폼 등의 과정을 거친후에 B 오프젝트를 렌더링하게 되며 GPU는 B를 렌더링 하게 된다.
여기서 관심있게 볼 것은 A보다 B의 오브젝트가 폴리곤이나 렌더링 부하가 더 많다고 가정했지만 DrawPrimitive 호출후 CPU에 프로세스 권한이 넘어오는 타이밍은 A나 B나 비슷하다는 것이다. GPU에게 명령을 주는 것은 폴리곤 수에 따라 하는 일에 큰 차이가 없다는 얘기다.

여기서 눈에 띄는 부분은 GPU가 A의 렌더링을 끝내고 대기하는 타이밍이다.
만약 B를 먼저 렌더링 한다면 어떻게 될까 ?


(그림4.2)

[그림4.2]처럼 된다. 즉 A의 작업은 비디오카드의 큐에 추가되고 비디오카드는 B의 렌더링이 끝나는 시점에 A를 렌더링하게 된다.

단순히 렌더링 타이밍에 맞춰서 배치만 조절해도 GPU가 대기하는 시간을 제거할 수 있다는 얘기다. 물론 위의 예처럼 GPU 타임을 예측해서 배치하는 것은 불가능하지만, [그림 4.3]처럼 단지 GPU가 부담을 느끼는 일을 먼저 던져준다면 별다른 처리 없이도 어느 정도의 효과를 얻을 수 있다.


(그림 4.3)

특히 CPU의 부담이 생길 수밖에 없는 배경 렌더링의 경우에는 배경 렌더링하기 전에 적절히 GPU에게 (좀 부담스러운 녀석으로) 할 일을 준다면 효과를 볼 수 있을 것이다. 항상 위의 그래프처럼 향상될 수도 있다는 얘기는 아니지만, 그렇지 않더라도 완충 효과를 줄 수 있어 대기 상태가 나타날 확률을 훨씬 줄일 수 있다. (배경보다 먼저 렌더링해도 전체적인 필레이트에 변화가 적다면 절대 손해 안보는 장사가 될 것이다. - 저라면 작게 렌더링 되면서, 폴리곤이 많은 녀석을 제물로 삼겠습니다.)

다만, 고려해야 할 것은 비디오의 큐 메모리도 한계가 있다는 것이다.
렌더링 타임이 긴 것들을 연속으로 넘기면 뒤의 처리들이 계속 큐게 쌓여서, 큐의 한계까지 차버리게 되는 데, 이 경우 큐에 넣기 위해 대기하는 시간이 생길 수 있다. (큐를 빨리 비우는 방법은 렌더링 타임이 작은 오브젝트를 찍는 것이므로 대기 시간이 안 생기고 큐에 무리를 주지 않는 범위에서 매니징을 해야 한다.)

한가지 주의할 것은 여기서 얘기하는 것은 순수하게 하드웨어의 힘으로 렌더링을 하는 경우여야 효과를 발휘할 수 있다는 점이다. 아닐 경우에는 GPU가 렌더링하는 시간이나 CPU가 처리하는 시간이나 비슷해져서 어떻게 해도 큰 차이가 없게 된다.

5. 최종 렌더링 타이밍

렌더링시 부하가 될만한 부분을 분리하여 구현하고 배치가 적절히 되었다면 [그림 5.1]처럼 나타낼 수 있다.


(그림 5.1)

한가지 짚고 넘어가면 보통 DISPLAY의 마지막에 호출되는 present나 flip 관련함수는 GPU 가 끝나기 전에 프로세스 권한을 넘겨주지 않는다. 즉 해당 함수에서는 큐가 빌 때까지 딜레이가 생기는 것이다. 실제로 DrawPrimitive 는 대부분 내부 큐에 렌더링할 꺼리를 쌓는 역할을 할 뿐이고 최종적으로 렌더링이 끝나는 것은 present 를 수행한 뒤가 된다.
(흔히 비 경험자들은 "프로파일링을 했더니 present함수가 가장 느리다." 라고 오해하기도 하는 데, 이는 직접적으로 present 함수의 부하가 아니므로 present 함수가 느리다고 생각하는 것은 적절한 판단은 아니다.)

인공지능, 물리학 등의 처리가 늘어날수록 process의 비중이 커져서 위의 구성으로는 GPU가 대기하는 시간이 비례해서 늘어나게 된다. 물론 위와 같이 배치하지 않고 렌더링 루틴 사이에 인공지능루틴들을 잘 넣으면, GPU가 대기하는 타이밍을 줄일 수도 있겠지만, 일정하게 효율을 얻기는 힘들기 때문에 추천하기는 어렵다.

결론부터 말하면 present 호출 후에 렌더링이 끝날 때까지 대기하는 시간에 CPU가 놀지 않고 다음 프레임의 process 를 실행하는 방법으로 이 문제를 해결할 수 있다.

#include <stdio.h>
#include <windows.h>

void _process()
{
   static int cnt=0;
   printf("process(%d)n", cnt++);
   Sleep(500); // process 부하
}

void _flip()
{
   Sleep(500); // flip 후 대기
}

void _display()
{
   static int cnt=0;
   static int time = timeGetTime();

   int delta = timeGetTime() - time;
   time += delta;

   printf("tdisplay (%d) %dn", cnt++, delta);
   Sleep(500); // 렌더링 명령

   _flip();
}

void main()
{
   int i;
   for(i=0; i<10; i++) {
       _process();
       _display();
   }
}

이해를 돕기 위해 가상적으로 위와 같은 파일을 구성해 봤다. 일반적인 구성을 만들어 본 것으로 process 가 500 ms, display 가 1000ms 의 부하를 가진다고 가정했다. (display는 500ms는 렌더링 명령하는 데 소비하는 시간, 500ms는 대기하는 시간이라고 가정했다.) 이를 실행해보면 다음과 같은 결과가 나온다.

process(0)
       display (0) 0
process(1)
       display (1) 1501
process(2)
       display (2) 1501
process(3)
       display (3) 1500
process(4)
       display (4) 1501

즉, _process와 _display에서 각 500ms, 1000ms 를 잡아먹기 때문에 한 프레임은 1500ms가 소요된다. 일반적인 경우라면 이 타이밍을 줄일 수는 없지만 [그림5.1]과 같다고 가정해보면 방법이 생긴다. 즉, _display의 렌더링 시간 중에 대기하는 시간에 다음 프레임의 _process 작업을 하는 것이다. 이 처리를 아래처럼 동기화 객체[2][3]를 이용하여 아래처럼 구현했다.

#include <stdio.h>
#include <windows.h>

HANDLE    g_hFlip, g_hRenderScene;

void _process()
{
   static int cnt=0;
   printf("process(%d)n", cnt++);
   Sleep(500);
}

void _flip()
{
   Sleep(500);
}

int g_done = 1;

DWORD WINAPI _flipper(void * ptr)
{
   WaitForSingleObjectEx(g_hFlip, INFINITE, FALSE);
   while(g_done) {
       _flip();
       SetEvent(g_hRenderScene);
       WaitForSingleObjectEx(g_hFlip, INFINITE, FALSE);
   }
   return 0;
}

void _display()
{
   static int cnt=0;
   static int time = timeGetTime();

   WaitForSingleObjectEx(g_hRenderScene, INFINITE, FALSE);

   int delta = timeGetTime() - time;
   time += delta;

   printf("tdisplay (%d) %dn", cnt++, delta);

   Sleep(500);

   SetEvent(g_hFlip);
}

void main()
{
   DWORD threadid;
   int i;

   g_hFlip = CreateEvent(NULL, FALSE, FALSE, NULL);
   g_hRenderScene = CreateEvent(NULL, FALSE, TRUE, NULL);

   CreateThread(NULL, 0, _flipper, 0, 0, &threadid);

   for(i=0; i<5; i++) {
       _process();
       _display();
   }

   CloseHandle(g_hFlip);
   CloseHandle(g_hRenderScene);
}

플리핑 함수만 처리하는 쓰레드를 두어서 이벤트 객체 g_hFlip로 _flip 을 컨트롤한다. 즉 처음엔 대기하다가 시그널을 주면 flip 함수를 실행하고 flip을 마친후엔 다음 시그널이 들어올때까지 블럭되어 대기하게 된다. 즉 _flip 함수 호출은 시그널을 주는 것으로 대체된다.
렌더링이 다되기도 전에 다음 프레임 렌더링하는 것을 방지하기 위해 g_hRenderScene 이벤트 객체를 두어서 흐름을 조정했다.

결과는 아래와 같다.

process(0)
       display (0) 0
process(1)
       display (1) 1001
process(2)
       display (2) 1001
process(3)
       display (3) 1002
process(4)
       display (4) 1001

즉 같은 내용을 실행하지만 전체적인 실행 간격을 줄일 수 있다는 얘기다.

보통 GPU 렌더링으로 대기하는 시간보다 process 시간이 작다면 이상적으로 GPU는 쉬지 않고 렌더링을 하게 되고, 최적의 프레임으로 렌더링이 가능해진다.

6. 결론

단순히 프레임을 극대화하는 방법은 아니지만, 물리학처리의 과부하, 네트워크 처리, 스크립트 호출 등으로 인한 불규칙한 프레임 저하에 좀 더 유연하도록 처리할 수 있고, 세세한 루틴 최적화에 대한 부담도 줄일 수 있다. (렌더링에 CPU가 관여하는 시간을 최소화했다면 최적화의 의미가 없을 수도 있겠다. - 물론 PC 플렛폼에서는 모든 하드웨어를 고려해야하기 때문에 생각하기 힘들지만...)

아주 기본적인 관계만을 고려한 것이지만 GPU를 최대한 활용하는 상황이라며 효율을 크게 높일 수 있는 방법이다.
점차 게임에서 처리하는 그래픽처리 규모나, 처리할 인공지능이나 물리학의 비중이 커지는 상황에서는 반드시 고려해야할 처리라고 생각된다.

(동기화 객체를 잘 이용한다면 프레임의 저하 없이 비동기적으로 리소스 로딩하는 루틴에도 활용할 수 있을 거 같네요. - 대기시간의 다음 프레임의 process 처리하고 남는 시간까지 활용해서...)

7. REFERENCE

[1] DirectX 9 Performance
   http://mirror.ati.com/developer/gdc/D3DTutorial3_Pipeline_Performance.pdf

[2] Win32 Multithreading and Synchronization
   http://blacksun.box.sk/tutorials.php/id/150

[3] Siberschatz. Operating System Concepts 6nd edition, chapter 7, WILREY, 2003


출처 : http://www.gamecode.org/tt/entry/1851
반응형
Posted by blueasa
, |

TLS - Thread Local Storage [ 펌 http://purewell.egloos.com/3398289]
쓰레드를 돌리다보면 해당 쓰레드에서만 유효한 변수를 만들고 싶을 때가 있다. 그럴 때 어떻게 해야할까? 쓰레드 개수만큼 벡터를 잡고, 해당 쓰레드마다 벡터 인덱스를 넘겨주는 것도 나름 방법이지만, 이러한 경우를 위해 TLS라는 것이 존재한다. 축약처럼 쓰레드 안에서만 따로 스토리지를 할당한 것인데, 일반 프로세스의 스텍과 비슷하나 그 크기는 프로세스 것보다 당연히 작고 제약이 심한 편이다. 뭐 암튼 그런게 있다고 치자.

* GCC 메뉴얼 보면 TLS가 아니라 TSD(Thread Storage Duration)이라고 표현하였는데 대충 같은 말이다.

이것을 위해 POSIX는 pthread_key_create, pthread_get/setspecific 등 함수를 마련해놨지만 눈만 팽글팽글 돌고, 소스만 지저분해져 보일 것 같다. 귀찮으면 C99, C++98 표준 __thread 키워드를 사용하자. MSDN에 보니 __thread가 없어 보인다. 이가 없으면 잇몸으로라도...
#define __thread __declspec( thread )
대충 이 정도면 될 듯하다. 자자 각설하고, __thread란 키워드는 TLS에 변수를 선언해주는 일을 한다. 위에 나열한 pthread_* 함수를 호출하는 것으로 컴파일러가 대신 해석해주는 것일 수도 있으나 이건 가정이므로 넘어가고. 사용에 주의해야할 점은 아래와 같다. (대충대충 표준하고 MSDN 보면서 정리한 것임)
  1. 글로벌 변수나 static 변수에만 사용 가능하다.
    - function 안에 local 변수가 static이 아니라면 뭐더게 TLS에 저장할까? 당연히 그럴 필요가 없기 때문에 저런 제약이 있는 것이다. 이는 class에서 멤버 변수에도 적용한 법칙이다. static으로 선언한 멤버 변수에만 저 키워드를 사용할 수 있다.
  2. SO(DLL)과 로컬 영역을 넘나들면 뻑난다.
    - ... 그러니까 쓰지마라. 왜?
  3. 타입에 대한 한정자로 사용이 불가능하다.
    - const처럼 쓰지 말라는 것이고, 무조건 타입 앞에 붙이란 말이다. (VC에만 해당)
  4. 객체 이름과 이름 없는 객체에만 쓸 수 있다.
  5. 앞에 붙을 수 있는 건 static과 extern 뿐이다.
  6. 클래스 자체를 TLS에 넣을 때는 생성자와 소멸자가 없어야 한다.
역시나 예가 없으면 아무 것도 할 수 없는 나를 위하여~*
#include <iostream>
using namespace std;

__thread int gTest1;      // 성공

__thread void func(void); // 실패

void
func(__thread int lTest2) // 실패
{
}

class A
{
    static __thread int mTest3;   // 성공
    __thread int mTest4;          // 실패
};

int
func(void)
{
    static __thread int lTest5; // 성공
    __thread int lTest6;   // 실패
}

__thread union
{
    int i;
    char p[sizeof(int)];
} gTest7;   // 성공

__thread struct
{
    int i;
    char p[sizeof(int)];
} gTest8;   // 성공

class MyClass
{
public:
    int i;
    MyClass() i(0) {}
};

__thread MyClass cTest9; // 실패

int __thread * gTest9; // gcc 성공

int
main(int,char**)
{
    return 0;
}
나머진 시간 나는대로 정리하도록 해야겠다.


출처 : http://blog.naver.com/kkan22?Redirect=Log&logNo=80057856857
반응형

'Programming > C/C++' 카테고리의 다른 글

GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
TCHAR을쓰면 바뀌는 함수들  (0) 2010.07.27
TCHAR printf  (0) 2010.07.12
A * pA = new B 과 B * pB = new B의 차이점.  (0) 2010.06.21
Posted by blueasa
, |
반응형
Posted by blueasa
, |

/FIXED[:NO]

설명

/FIXED 옵션을 사용하면 운영 체제에서는 기본 설정 기준 주소에서만 프로그램을 로드하게 됩니다. 기본 설정 기준 주소를 사용할 수 없으면 파일이 로드되지 않습니다. 자세한 내용은 /BASE(기준 주소)를 참조하십시오.

기본적으로 DLL을 빌드할 때는 /FIXED:NO가 기본값이 되고 다른 모든 프로젝트 형식에 대해서는 /FIXED가 기본값이 됩니다.

/FIXED를 지정하면 LINK에서는 프로그램에 재배치 섹션을 생성하지 않습니다. 런타임에 운영 체제가 해당 주소에서 프로그램을 로드할 수 없으면 오류 메시지가 표시되며 프로그램은 로드되지 않습니다.

프로그램에 재배치 섹션을 생성하려면 /FIXED:NO를 지정합니다.

Windows NT용 장치 드라이버를 빌드할 때는 /FIXED를 사용하지 마십시오.

Visual Studio 개발 환경에서 이 링커 옵션을 설정하려면

  1. 프로젝트의 속성 페이지 대화 상자를 엽니다. 자세한 내용은 Visual C++ 프로젝트 속성 설정을 참조하십시오.

  2. 링커 폴더를 클릭합니다.

  3. 명령줄 속성 페이지를 클릭합니다.

  4. 추가 옵션 상자에 옵션을 입력합니다.

프로그래밍 방식으로 이 링커 옵션을 설정하려면

반응형
Posted by blueasa
, |

/FORCE:[MULTIPLE|UNRESOLVED]

설명

/FORCE 옵션을 사용하면 링커에서는 기호가 참조되어 있지만 정의되어 있지 않은 경우나 여러 번 정의되어 있는 경우에도 올바른 .exe 파일 또는 DLL을 만듭니다.

/FORCE 옵션에는 다음과 같이 선택적 인수를 사용할 수 있습니다.

  • /FORCE:MULTIPLE을 사용하면 LINK에서 기호에 대한 정의를 하나 이상 찾더라도 출력 파일을 만들 수 있습니다.

  • /FORCE:UNRESOLVED를 사용하면 LINK에서 정의되지 않은 기호를 찾더라도 출력 파일을 만들 수 있습니다. 진입점 기호가 확인되지 않으면 /FORCE:UNRESOLVED는 무시됩니다.

/FORCE에 인수를 사용하지 않으면 MULTIPLE과 UNRESOLVED를 모두 의미합니다.

이 옵션을 사용하여 만든 파일이 예상대로 실행되지 않을 수도 있습니다. 링커에서는 /FORCE 옵션이 지정된 경우 증분 링크를 수행하지 않기 때문입니다.

/clr를 사용하여 모듈을 컴파일하는 경우 /FORCE를 지정해도 이미지가 생성되지 않습니다.

Visual Studio 개발 환경에서 이 링커 옵션을 설정하려면

  1. 프로젝트의 속성 페이지 대화 상자를 엽니다. 자세한 내용은 Visual C++ 프로젝트 속성 설정을 참조하십시오.

  2. 링커 폴더를 클릭합니다.

  3. 명령줄 속성 페이지를 클릭합니다.

  4. 추가 옵션 상자에 옵션을 입력합니다.

프로그래밍 방식으로 이 링커 옵션을 설정하려면

반응형
Posted by blueasa
, |

How To Be A Programmer

Etc / 2010. 8. 23. 19:31
반응형
Posted by blueasa
, |
현재 전 Dina 폰트, 크기 8로 쓰고 있습니다.

흰색이 눈아파서 아래 배경으로 바꿔봤는데 눈 부담이 덜하네요.

좀 써봐야지..

배경 RGB : 226, 234, 245

출처 : http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talk&page=1&sn1=&divpage=2&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=6299
반응형
Posted by blueasa
, |

 
<컨트롤 속성>
DataGridView ID: dbView
 
1. 홀수행을 다른 색으로 보여주고 싶을 때
행마다 특정 배경색을 입혀보고 싶은 분들은 다음과 같이 구현하시면 됩니다. 일단 홀수행을 기준으로 설명하자면 AlternatiogRowsDefaultCellStyle속성에 BackColor를 선언하여 색상을 지정하시면 됩니다.
 
dbView.AlternatingRowsDefaultCellStyle.BackColor = Color.Aqua;
 
2. 여러개의 열이나 행을 선택하지 못하도록 막고 싶을 때
기본적으로 여러개의 열과 행을 선택할 수 있는데 이를 막고 한개의 열이나 한개의 행만 선택하도록 하고 싶을 때 다음과 같이 선언하면 됩니다.
 
dbView.MultiSelect = false;
 
3. 행단위로 클릭하도록 만들고 싶을 때
기본적으로 열단위로 클릭하도록 처리되어 있는데 이를 행단위로 클릭할 수 있는 기능도 있습니다. 이 기능은 SelectionMode속성에 DataGridViewSelectionMode.FullRowSelect를 설정하면 간단하게 됩니다.
 
dbView.SelectionMode = DataGridViewSelectMode.FullRowSelect;
 
참고로 DataGridViewSelectMode열거체이외의 값도 설정하여 열단위로 선택기능을 구현할 수 있고 열단위로 선택기능을 사용시 각 열에 대한 SortMode속성이 Automatic값(기본)으로 설정이 안되어 있으면 사용할 수 없습니다.
 
4. 행번호 보여주고 싶을 때
각 행의 행번호를 보여주고 싶을 때에는 행을 화면에 보여주는 타이밍인 RowPostPaint이벤트 타이밍에 그 행의 인덱스번호+1형태로 행헤더의 열 안에 넣어주면 끝납니다.
RowPostPaint이벤트핸들러의 2번째 파라미터인 DataGridViewRowPostPaintEventArgs 객체로부터 보여주기 위해 필요한 Graphics객체나 좌표값을 얻을 수 있습니다.
이하 e변수로 받을 수 있는 객체에 대해서 알아보면 다음과 같습니다.
 
<DataGridViewRowPostPaintEventArgs 객체>
  * e.Graphics - Graphics객체
  * e.RowIndex - 표시중인 행번호 (0부터 시작하기 떄문에 +1필요) 
  * e.RowBounds.X 행헤더 열 왼쪽 위 X좌표
  * e.RowBounds.Y 행헤더 열 왼쪽 위 Y좌표
  * e.RowBounds.Height 행헤더 열높이
  * dbView.RowHeadersWidth 행헤더 셀 폭
  * dbView.RowHeadersDefaultCellStyle.Font 사용폰트
  * dbView.RowHeadersDefaultCellStyle.FontColor 폰트 색상
  
private void dbView_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) {
     // RowPointPaint 이벤트핸들러
     // 행헤더 열영역에 행번호를 보여주기 위해 장방형으로 처리
     Rectangle rect = new Rectangle(e.RowBounds.Location.X,
                         e.RowBounds.Location.Y,
                         dbView.RowHeadersWidth - 4,
                         e.RowBounds.Height);
     // 위에서 생성된 장방형내에 행번호를 보여주고 폰트색상 및 배경을 설정
     TextRenderer.DrawText(e.Graphics,
                         (e.RowIndex + 1).ToString(), 
                         dbView.RowHeadersDefaultCellStyle.Font,
                         rect,  
                         dbView.RowHeadersDefaultCellStyle.ForeColor,
                         TextFormatFlags.VerticalCenter | TextFormatFlags.Right);
}
 
5. 특정 행이나 열을 고정시키고 스크롤하지 못하도록 막고 싶을 때
여기서는 2번째 열을 고정시킨 경우를 보여주고 있는데 그리드를 옆으로 스크롤 할 경우 3번째 열보다 우측의 스크롤이 나오고 1번째열과 2번째열은 고정된 형태를 보여줍니다.
 
- Frozen 속성 : 열 고정시키기 위한 속성으로 이를 고정시키기 위한 열을 지정하고 true값을 선언하면 해당 열의 좌측 열은 전부 고정되어 스크롤 할 없게 됩니다.
 
dbView.Columns[1].Frozen = true;
 
- DividerWidth 속성 : 구분선 폭 변경을 할 때 사용하는 속성으로서 이를 통해서 2번째열과 3번째열 사이 구분선을 약간 두껍게 표현하여 보다 직관적으로 구분할 수 있습니다.
 
dbView.Columns[1].DividerWidth = 3;
 
6. 그리드 실행될 때 기본 열이 선택된 모습을 보여주고 싶지 않을 때
아주 간단하게 속성하나만 선언하면 되는데 이때 주의해야할 점은 폼_Load이벤트핸들러에서는 효과가 없다는 것입니다. 어느 열도 선택되지 않는 상태로 보여주기 위해서는 폼이 표시된 이후 한번만 발생하는 폼_Shown 이벤트핸들러에서 선언해주는 것이 좋습니다.
 
        private void Form1_Shown(object sender, EventArgs e)
        {
            //6. 그리드 실행될 때 기본 열이 선택된 모습을 보여주고 싶지 않을 때
            dbView.CurrentCell = null;
        }
 
7. 특정값을 가진 열을 좀 다르게 보여주고 싶을 때
특정값을 가진 열을 좀더 강조하여 보여주고 싶은 경우가 종종 생깁니다. 이럴 때 유용한 것으로 예제에서는 Bike란 단어가 포함된 열인 경우 빨간색으로 글씨를 보여주도록 설정하였습니다.
특히, 개별적인 열에 대해서 보여주기 위해서는 폼_CellFormating이벤트핸들러를 이용하면 될것같습니다. 본래 이 이벤트핸들러는 특정값을 가진 열의 독립적인 서식을 적용하기 위한 것으로 열 스타일만 변경할 수 있습니다.
CellFormmating이벤트핸들러의 2번째파라미터로 받는 DataGridViewCellFormattingEventArgs객체로부터 그 열의 현재 스타일이나 열의 값을 얻거나 설정할 수 있습니다. 즉, 이것을 통해 열의 값을 확인하고 그 스타일을 변경하는 형태로 개발을 해보려고 합니다. 또한, 이벤트 발생시 열위치는 ColumnIndex속성과 RowIndex속성으로 알 수 있습니다.
 
private void dbView_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e) {
{
      // 7. 특정값을 가진 열을 좀 다르게 보여주고 싶을 때
      if (e.ColumnIndex == 1)
      {
            if (e.Value != null)
            {
                 string text = e.Value.ToString();
                 if (text.Contains("Bike"))
                 {
                      e.CellStyle.ForeColor = Color.Red;
                      e.CellStyle.SelectionForeColor = Color.Red;
                  }
              }
         }
  }
 
8. 헤더열이나 헤더행의 색을 변경하고 싶을 때
헤더행과 헤더열의 색을 변경하고싶은 경우에는 ColumnHeadersDefaultCellStyle속성과 RowHeadersDefaultCellStyle속성을 사용하면 괜찮을 것같습니다.
 
dbView.ColumnHeadersDefaultCellStyle.BackColor = Color.RosyBrown;
dbView.RowHeadersDefaultCellStyle.BackColor = Color.SeaGreen;
 
단, 위와 같이 선언하여도 그것이 사용할 수 없는 경우가 있는데 이것은 애플리케이션이 적용하고 있는 Windows XP의 Visual 스타일을 우선적으로 사용하기 때문인데 이 코드는 윈폼을 만들 때 자동으로생성하기 때문에 Application클래스의 EnableVisualStyles메소드를 호출합니다.
 
dbView.EnableHeadersVisualStyles = false;
 
그외 열과 행의 사이에 있는 선스타일을 변경할 수 있는데 다음과 같이 선언하게 되면 한개의 라인으로 보여주게 됩니다.
 
dbView.RowHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
dbView.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
 
9. 선택한 행이나 열에 대한 값을 확인하고 싶을 때
보통 특정 행을 추가하거나 삭제하고 편집도 하는데 이럴 때 선택된 행이나 열에 대해서 어떤 조작을 하는 경우도 많습니다. 그래서 이번에는 이를 간단하게 처리하는 방법에 대해서 정리해보았습니다. 이 부분에 대한 것을 사용형태만 설명하고 넘어가겟습니다.
 
아래에서 설명한 것들중에서 같은 클래스명이나 속성명이 Row가 들어가거나 Column이란 단어로 비교되는 것을 알 수 있는데 이것에 의해 열에 대한 행과 똑같은 조작을 할 수 있다는 것을 알 수 있습니다.
 
- 선택된 열 얻기 : SelectedCells 속성
현재 선택된 열은 SelectedCells속성으로 얻을 수 있고 그리드에서 여러개의 열을 동시에 선택할 수 있기 때문에 이 속성은 DataGridViewSelectedCellCollection클래스 객체로 되어 잇습니다. 여기서는 간단하게 하나의 열을 조작할 경우에 사용되는 형태를 보여줍니다.
 
foreach (DataGridViewCell cell in dbView.SelectedCells) { }
 
선택된 열 갯수 얻기을 얻어 위에서 선택된 열을 얻을 수 있는 방법도 있습니다.
 SelectedCells.Count
for(int i=0; i<dbView.SelectedCells.Count; i++) { }
 
또한 MultiSelect속성을 false를 설정한 것은 사용자가 여러개의 행/열을 선택할 수 있도록 할 수 있습니다.
 
- 선택된 행 얻기 : SelectedRows 속성
행을 선택하여 행정보를 얻는 경우에 필요한 것으로 DataGridViewRow클래스의 객체의 컬렉션은 Selectedows속성으로 얻을 수 있습니다.
 
foreach(DataGridViewRow row in dbView.SelectedRows) { }
 
- 특정 셀이나 행이 선택되었는지 확인하는 경우 : Selected 속성
셀이 선택되었다면 true, 셀이 선택되지 않았다면 false로 결과를 보내줍니다.
 
dbView[x,y].Selected;
 
특정 행이 선택되었는지 확인하는 경우
 
dbView.Rows[n].Selected;
 
- 선택된 행이나 열 설정
데이터 새로고침등의 이벤트로 인해 데이터를 새롭게 보여줄때 현재 위치를 나타내고 싶은 경우에 필요한 것으로 (x,y)위치에 있는 열을 선택한 상태로 두고 싶다면 다음과 같이 선언합니다.
 
dbView[x,y].Selected = true;
 
n행의 행을 선택한 상태로 두고 싶은 경우에는 아래와 같이 선언합니다.
 
dbView.Rows[n].Selected = true;
 
- 모든 선택된 열을 지울 때 : ClearSelection 메소드
 
10. 열에 보여지는 문자열을 여러행으로 보여주고 싶을 때
열에 표시되는 문자열이 길게 되면 한줄로 보여주지만 오버되면 잘려서 안보여줍니다. 이는 열스타일의 WrapMode속성을 DataGridViewTriState.True(그외 False, NotSet)로 설정하여 셀크기안에서 오버되면 여러행으로 해당 텍스트등을 모두 보여줍니다.
그리드안의 모든 열을 여러행을 보여주고 싶다면, DefaultCellStyle속성의 WrapMode속성에 DataGridViewTriState.True를 설정하는 것을 권장합니다. 이렇게하면 정말 간단하게 구현이 됩니다. ^^
또한, 다음줄로 넘어간 문자열이 발생하면 행의 높이는 자동으로 조절되는데 AutoSizeRowMode속성에 DataGridViewAutoSizeRowsMode.AllCells를 설정할 것을 권장합니다. 이상 이 두가지를 정리하면 다음과 같습니다.
 
dbView.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
dbView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
 
11. 맨 밑까지 스크롤하고 싶을 때
보통 맨 밑에 있는 행을 합계로 보여주거나 응용프로그램 실행시 곧바로 사용자가 새 행을 추가할 있도록 하는 경우가 있는데 이럴때 그리드 맨밑까지 스크롤을 가볍게 해줄 필요가 있습니다. 열단위 표시를 스크롤하기 위한 속성이 3가지가 있는데 다음과 같습니다.
 
<속성>
FirstDisplayedCell 그리드 왼쪽 위에 표시된 열 얻기/설정
FirstDisplayedScrollingColumnIndex 그리드에 표시된 처음 열 얻기/설정
FirstDisplayedScrollingRowIndex 그리에 표시된 처음 행 얻기/설정
 
예를 들어 [2,15]의 위치에 있는 열을 FirstDisplayedCell속성으로 설정하면 [2,15]위치의 열에 왼쪽위로 그리드가 스크롤된 상태로 보여집니다.
 
dbView.FirstDisplayedCell = dbView[2,15];
 
마지막 행을 표시하기 위해 그리드 맨밑까지 스크롤시키려면 FirstDisplayedScrollingRowIndex속성을 이용하길 권장합니다.
 
dbView.FirstDisplayedScrollingRowIndex = dbView.Rows.Count - 1;
 
12. 오토컴플릿 기능을 사용하고 싶을 때
텍스트박스 열은 열 편집 및 새 행을 추가시 텍스트박스를 사용합니다. 이럴때 열편집시 오토컴플릿 기능을 사용할 수 있다는 점입니다.
이는 열이 편집상태가 될 때 발생하는 EditingControlShowing이벤트핸들러가 발생할 때 보여주고 있는 텍스트박스 객체를 얻어 오토컴플릿에 필요한 속성을 설장하는 형태입니다.
실제 이 텍스트박스는 DataGridViewTextBoxEditingControl 클래스의 인스턴스를 만들어 TextBox클래스의 파생클래스로서 TextBox형으로 케스팅합니다.
이렇게 예고로 내놓은 코드는 모든 텍스트박스에 대한 오토컴플릿기능을 설정하고 있기 때문에 어느 열을 사용하더라도 오토컴플릿기능이 작동되며 특정열만 오토컴플릿 기능을 지정할 수도 있습니다. 이 예제에서는
 
 private void dbView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
 {
        TextBox tb = e.Control as TextBox;
        if (tb == null)
        {
             return;
         }
        if (dbView.CurrentCell.ColumnIndex == 1)
        {
             tb.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
             tb.AutoCompleteSource = AutoCompleteSource.AllSystemSources;
         }
         else
         {
             tb.AutoCompleteMode = AutoCompleteMode.None;
         }
  }  
 
13. 열안에 문자열 선택하고 싶을 때
열 안에의 문자열을 선택하여 처리하고 싶다면 BeginEdit메소드를 사용하는 것을 권장합니다. 이 이름처럼 열을 편집하기 위한 것으로 파라미터값을 true로 선언하면 열 안의 문자열이 선택된 상태로 편집할 수 있습니다. 만약 false로 선언하면 커서가 문자열은 선택하지 않고 문자열끝에 커서가 있는 상태로 조작을 할 수 있습니다.
 
 // 폼_Load이벤트핸들러가 BeginEdit를 호출할 경우 폼을 먼저 표시할 필요가 있음
 this.Show();
 // 2행 1열 열을 현재 열로 지정
 dbView.CurrentCell = dbView[0, 1];
 // 현재 열을 편집상태로
 dbView.BeginEdit(true);
 
14. 왼쪽 윗 빈 열에 값 설정하고 싶을 때
왼쪽 맨 윗열은 항상 빈 여백의 열로 남아 있는데 여기에 특정한 표시를 나타내기 위해 값을 넣거나 열 스타일을 변경할 수 있습니다.
이 빈열은 DataGridView컨트롤의 TopLeftHeaderCell속성으로 접근할 수 있고 이 속성값은 헤더 행이나 헤더 열과 동일한 DataGridViewHeaderCell클래스의 객체입니다. 일반적인 열을 나타내는 DataGridViewCell클래스를 상속받은 클래스입니다.
 
dbView.TopLeftHeaderCell.Value = "매장";
 
15. 오른쪽버튼 클릭시 열 선택을 할 수 있도록 하고 싶을 때
기본적으로 마우스 오른쪽버튼으로 클릭시 어떠한 조작도 제어되지 않습니다. 그래서 간단하게 클릭시 열을 선택할 수 있도록 하는 방법을 추가해보고자 합니다.
이는 CellMouseClick이벤트핸들러를 생성하여 열을 클릭시 Control클래스로 정의되어 있는 MouseClick이벤트가 추가되어 CellMouseClick이벤트핸들러가 발생하는 형태입니다. 이 이벤트핸들러의 파라미터로는 클릭된 열의 행번호와 열번호를 얻을 있어 클릭되어 진 열을 간단하게 접근하실 수 있습니다.
여기서 보여주는 스타일은 이벤트핸들러를 통해 클릭된 열의 선택상태를 알려주는 것을 아주 간단합니다.
 
 private void dbView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e) {
      //오른쪽 버튼 클릭인가?
      if (e.Button == MouseButtons.Right) {
           // 헤더 이외의 열
          if (e.ColumnIndex >= 0 && e.RowIndex >= 0) {
               // 오른쪽 버튼 클릭된 열
               DataGridViewCell cell = dbView[e.ColumnIndex, e.RowIndex];
               cell.Selected = !cell.Selected; // 선택상태 반전
           }
      }
  }  
한가지더 알려드리자면, 두번째 파라미터 DataGridViewCellMouseEventArgs클래스는 MouseClick이벤트핸들러의 파라미터인 MouseEventArgs클래스의 파생클래스입니다. 이 클래스는 마우스의 위치나 클릭된 버튼 종류를 알려주며 클릭된 열의 행번호와 열번호를 리턴해주는 RowIndex속성과 ColumnIndex속성이 추가되어 있고 헤더용 열이나 행이 클릭된 경우 속성은 -1로 처리됩니다.
 
16. 행 삭제시 확인메세지를 보여주고 싶을 때
행을 선택하고 Del키를 누르면 행을 삭제할 수 있습니다. 이럴 때 행의 삭제시 확인 메세지박스를 보여주는 형태를 구현하고 싶은 분들이 있을 것입니다. 이를 한번 구현해보이도록 하겠습니다.
DataGridView컨트롤에서는 행이 삭제될 때 UserDeletingRow이벤트핸들러가 발생합니다. 따라서 이 타이밍에 확인 메세지박스를 보여주는 것을 좋을 것같습니다. 그리고 한가지 팁으로 여러개의 행을 선택하고 Del키를 누르면 선택되어 있는 각 행별로 UserDeletingRow이벤트핸들러가 발생합니다.
또한 이 UserDeletingRow이벤트핸들러의 두번째 파라미터는 DataGridViewRowCancelEventArgs객체의 Cancel속성을 true로 설정하여 행삭제를 취소할 수 있습니다.
 
private void dbView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) {
       if (MessageBox.Show("삭제하시겠습니까?",
                                 "지정한 행삭제",
                                 MessageBoxButtons.YesNo) == DialogResult.No)
       {
             e.Cancel = true; //삭제 취소
       }
 }  
 
17. 열 순서를 마음대로 바꾸고 싶을 때
가끔 고객의 요구로 인해 열을 사용자가 원하는 순서대로 배치하고 싶은 경우가 나옵니다. 이럴때 이런 기능을 모르면 대략 난감했죠 ㅜ_ㅜ; 근데 DataGridView에서는 간단하게 되네요 ^^; 보통 열 순서변경은 마우스로 열 헤더를 드래그&드롭하여 처리할 수 있습니다.
AllowUserToOrderColumns속성을 true로 하면 간단하게 끝납니다. ㅡ_ㅡ;;;
 
dbView.AllowUserToOrderColumns = true;

출처 : http://sdj.kr/g4/bbs/board.php?bo_table=i_css&wr_id=6
반응형
Posted by blueasa
, |

C# 관련 블로그

Link / 2010. 8. 18. 17:35
반응형

'Link' 카테고리의 다른 글

Dot Net Perls  (0) 2010.11.08
CppCheck(소스 결함 예방 툴)  (0) 2010.10.06
Visual Assist X 관련 링크~  (0) 2010.06.21
게임브리오 관련 사이트  (0) 2010.06.07
C++/CLI 참고 하는 사이트  (0) 2010.06.03
Posted by blueasa
, |

ref 또는 out을 사용하지 않고 메서드의 매개 변수를 선언하면 매개 변수에 연결된 값을 할당할 수 있습니다. 메서드에서 이 값을 변경할 수 있지만 호출한 프로시저로 제어가 다시 전달되면 변경된 값은

유지되지 않습니다. 메서드 매개 변수 키워드를 사용하면 이러한 동작을 변경할 수 있습니다.

이 단원에서는 메서드 매개 변수를 선언할 때 사용할 수 있는 아래와 같은 키워드에 대해 설명합니다.

params(C# 참조)

params 키워드를 사용하면 인수 갯수가 변수로 주어지는 인수를 사용하는 메서드 매개 변수를 지정할 수

있습니다.

메서드 선언에서 params 키워드 다음에는 매개 변수를 추가할 수 없으며 params 키워드 하나만 메서드

선언에 사용할 수 있습니다.

// cs_params.cs using System;

public class MyClass { public static void UseParams(params int[] list) { for (int i = 0 ; i < list.Length; i++) { Console.WriteLine(list[i]); } Console.WriteLine(); } public static void UseParams2(params object[] list) { for (int i = 0 ; i < list.Length; i++) { Console.WriteLine(list[i]); } Console.WriteLine(); } static void Main() { UseParams(1, 2, 3); UseParams2(1, 'a', "test"); // An array of objects can also be passed, as long as // the array type matches the method being called. int[] myarray = new int[3] {10,11,12}; UseParams(myarray); } }

출력

1 2 3 1 a test 10 11 12

 

 

ref(C# 참조)

ref 키워드는 인수를 참조로 전달하는 데 사용됩니다.

메서드의 모든 매개 변수 변경 사항은 호출하는 메서드로 제어가 다시 전달될 때 해당 변수에 반영됩니다.

ref 매개 변수를 사용하려면 메서드 정의와 호출하는 메서드에서 모두 ref 키워드를 명시적으로

사용해야 합니다. 예를 들면 다음과 같습니다.

 

 예제

 

class RefExample
{
    static void Method(ref int i)
    {
        i = 44;
    }
    static void Main()
    {
        int val = 0;
        Method(ref val);
        // val is now 44
    }
}

ref 매개 변수에 전달되는 인수는 먼저 초기화되어야 합니다.

이는 해당 인수를 전달하기 전에 명시적으로 초기화할 필요가 없는 out과 다른 점입니다.

out을 참조하십시오.

 

refout은 런타임에 서로 다르게 취급되지만 컴파일 타임에는 동일하게 취급됩니다.

따라서 한 메서드는 ref 인수를 사용하고 다른 메서드는 out 인수를 사용하는 경우 메서드를

오버로드할 수 없습니다.

 

예를 들어, 이러한 두 메서드는 컴파일할 때 동일하게 간주되므로 다음과 같은 코드는 컴파일되지

않습니다.

class CS0663_Example 
{
    // compiler error CS0663: "cannot define overloaded 
    // methods that differ only on ref and out"
    public void SampleMethod(ref int i) {  }
    public void SampleMethod(out int i) {  }
}

그러나 한 메서드가 ref 또는 out 인수를 사용하고 다른 메서드는 두 인수 중 어느 것도 사용하지

않는 경우 다음과 같이 오버로드할 수 있습니다.

 

class RefOutOverloadExample { public void SampleMethod(int i) { } public void SampleMethod(ref int i) { } }

속성은 변수가 아니므로 ref 매개 변수로 전달할 수 없습니다.

배열을 전달하는 방법에 대한 자세한 내용은 ref 및 out을 사용한 배열 전달을 참조하십시오.

위 예제에서와 같이 값 형식을 참조로 전달하는 방법 이외에 ref를 사용하여 참조 형식을 전달할 수도

있습니다.

이렇게 하면 참조 자체가 참조로 전달되므로 호출된 메서드에서 참조가 가리키는 개체를 수정할 수

있습니다.

다음 샘플에서는 참조 형식이 ref 매개 변수로 전달될 때 개체 자체가 변경될 수 있음을 보여 줍니다.

 

class RefRefExample
{
    static void Method(ref string s)
    {
        s = "changed";
    }
    static void Main()
    {
        string str = "original";
        Method(ref str);
        // str is now "changed"
    }
}
out(C# 참조)

out 키워드는 인수를 참조로 전달하는 데 사용됩니다.

이는 ref 키워드와 비슷하지만 ref의 경우 변수를 전달하기 전에 초기화해야 한다는 점에서 차이가

있습니다.

out 매개 변수를 사용하려면 메서드 정의와 호출하는 메서드에서 모두 out 키워드를 명시적으로

사용해야 합니다.

 

예를 들면 다음과 같습니다.

 

class OutExample
{
    static void Method(out int i)
    {
        i = 44;
    }
    static void Main()
    {
        int value;
        Method(out value);
        // value is now 44
    }
}

out 인수로 전달되는 변수는 이를 전달하기 전에 초기화할 필요가 없지만 호출하는 메서드는

메서드 반환 이전에 값을 할당해야 합니다.

 

refout 키워드는 런타임에 서로 다르게 취급되지만 컴파일 타임에는 동일하게 취급됩니다.

따라서 한 메서드는 ref 인수를 사용하고 다른 메서드는 out 인수를 사용하는 경우 메서드를

오버로드할 수 없습니다.

 

예를 들어, 이러한 두 메서드는 컴파일할 때 동일하게 간주되므로 다음과 같은 코드는

컴파일되지 않습니다.

 

class CS0663_Example 
{
    // compiler error CS0663: "cannot define overloaded 
    // methods that differ only on ref and out"
    public void SampleMethod(out int i) {  }
    public void SampleMethod(ref int i) {  }
}

그러나 한 메서드가 ref 또는 out 인수를 사용하고 다른 메서드는

두 인수 중 어느 것도 사용하지 않는 경우 다음과 같이 오버로드할 수 있습니다.

 

class RefOutOverloadExample { public void SampleMethod(int i) { } public void SampleMethod(out int i) { } }

 

속성은 변수가 아니므로 out 매개 변수로 전달할 수 없습니다.

배열을 전달하는 방법에 대한 자세한 내용은 ref 및 out을 사용한 배열 전달을 참조하십시오.

메서드에서 값을 여러 개 반환해야 하는 경우 out 메서드를 선언하는 것이 좋습니다.

out 매개 변수를 사용하는 메서드에서는 변수를 반환 형식으로 사용할 수 있지만(return 참조)

호출하는 메서드에 하나 이상의 개체를 out 매개 변수로 반환할 수도 있습니다.

다음 예제에서는 out을 사용하여 한 번의 메서드 호출로 변수 세 개를 반환합니다.

세 번째 인수에는 null이 할당됩니다. 이렇게 하면 메서드에서 값을 선택적으로 반환할 수 있습니다.

 

class OutReturnExample
{
    static void Method(out int i, out string s1, out string s2)
    {
        i = 44;
        s1 = "I've been returned";
        s2 = null;
    }
    static void Main()
    {
        int value;
        string str1, str2;
        Method(out value, out str1, out str2);
        // value is now 44
        // str1 is now "I've been returned"
        // str2 is (still) null;
    }
}
반응형

'Programming > C#' 카테고리의 다른 글

String과 string의 차이..??  (0) 2010.08.27
DataGridView 활용 Tip  (0) 2010.08.19
datagrid에서 클릭하면 한 줄이 선택되도록 할 수 있나요?  (0) 2010.08.17
DataSet 및 DataGrid 관련 팁  (0) 2010.08.17
File과 Directory, Path  (0) 2010.08.16
Posted by blueasa
, |