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

카테고리

분류 전체보기 (2799)
Unity3D (855)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
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

TinyXML Read

Programming/TinyXML / 2010. 12. 8. 14:43

C++용 XML 라이브러리가 몇가지 있다. MSXML 라이브러리, TinyXML 라이브러리가 있다.
작고 빠르다고 알려진 TinyXML 라비브러리를 알아 본다.

내가 사용한 버전은 TinyXML 2.5.3 버전이다. xmltet.cpp는 메모장에서 파일 인코딩을
UTF-8로 열고 유니코드 인코딩으로 재 저장하여 문자가 깨지지 않도록 한다.

TinyXML의 NodeType

TinyXml의 NodeType은 XML의 노드 타입과 대부분 비슷 하다.
XML 서적이나 문서를 보면 주석, 속성, TEXT, Element들의 최소단위는 노드이다. 
엘리먼트와 노드를 혼동하지 않도록 한다. 
TinyXML에서는 TiXmlElement노드가 TiXmlNode를 상속하고 있다.  

COMMENT 노드: 주석을 표현 하는 노드

ELEMENT 노드: 엘리먼트를 표현 하는 노이다.
              Test.xml에서는 root, food, name, country가 엘리먼트이다.
              (test.xml 파일은 아래에 나온다.)

TEXT 노드: 트리 구조상 최말단 노드로 문자 데이터를 표현하는 노드이다.

Attribute node: TinyXml 노드 타입에는 존재하지 않지만 TinyXml에서는 속성을 얻어                 올수 있는 메소드를 제공한다.

나머지 node 타입은 문서를 참고한다.

 

헤더 파일 포함

TinyXML 내부에서 자체 string 클래스 대신 std::string 클래스를 사용 할려면,
TIXML_USE_STL을 선언한다.
STL 사용에 필요한 헤더를 포함 시킨다.

#define TIXML_USE_STL    // TinyXML의 string대신 std::string을 사용할 경우.

#include “tinyxml.h”

 

#ifdef TIXML_USE_STL

    #include <iostream>

    #include <sstream>

    using namespace std;

#else

    #include <stdio.h>

#endif

 

라이브러리 링크

TIXML_USE_STL에 따라 라이브러리 링크를 다르게 설정한다.

//lib 파일 링크

 

#ifdef TIXML_USE_STL

 

#ifdef _DEBUG

#pragma comment(lib, "tinyxmld_STL")

#else

#pragma comment(lib, "tinyxml_STL")

#endif

 

#else

 

#ifdef _DEBUG

#pragma comment(lib, "tinyxmld")

#else

#pragma comment(lib, "tinyxml")

#endif

 

#endif

test.xml

읽을려는 XML 파일이다.

<?xml version="1.0" encoding="euc-kr"?>

<root>

  <!--각국의 대표 음식들이다.-->

  <food num="1">

      <name>김치</name>

      <country>대한민국</country>

  </food>

  <food num="2">

    <name>햄버그</name>

    <country>미국</country>

  </food> 

</root>

파일을 통해 읽을려면

    TiXmlDocument doc;

    doc.LoadFile("test.xml");

문자열을 통해 읽을려면

    TiXmlDocument doc;

    doc.Parse(szXML);

root 엘리먼트 노드를 읽는다.

TiXmlElement* rootElement = doc.FirstChildElement( "root" );

"root"의 자식 "food" 엘리먼트 노드를 읽는다.

    TiXmlElement*  element = doc.FirstChildElement( "root" )->FirstChildElement("food");

"food" 엘리먼트 노드를 반복해서 출력 한다.

    1     while( element )

    2     {

    3         //Value()는 현재 Node의 문자열을 넘겨준다.

    4         //GetText()는 현재 Text 타입 Node의 문자열을 넘겨 준다.

    5         //element->FirstChildElement()->Value()는 "name"을 넘겨준다.

    6         const char* num = element->Attribute( "num" );

    7         std::cout << "num Attribute: " << num << std::endl;

    8         std::cout << "name: " << element->FirstChildElement("name")->GetText() << std::endl;

    9         std::cout << "country: " << element->FirstChildElement("country")->GetText() << std::endl;

   10         element = element->NextSiblingElement( "food" );

   11     }

1: "food" 엘리먼트가 있을 때 까지 반복한다.

6: "num" 속성의 문자열을 읽어 온다.

속성을 정수로 읽을려면 다음과 같이 한다.
TiXmlAttribute* attr = element->FirstAttribute();
int n = attr->IntValue();

7: "name" 엘리먼트의 값을 읽는다.

8: "country" 엘리먼트의 값을 읽는다.

10: 다음 "food" 엘리먼트로 이동 한다.

 

XML 전체 파싱

ShowXML()은 XML을 파싱하여 보여주는 함수이다.
코드 설명은 생략....

void ShowXml(TiXmlNode *pNode)

{

       if(pNode)

       {

              switch(pNode->Type())

              {

              case TiXmlNode::COMMENT:

                    std::cout << "COMMENT: " << pNode->Value() << std::endl;

                    break;

              case TiXmlNode::DECLARATION:

                    std::cout << "DECLARATION: " << std::endl;

                    break;

 

              case TiXmlNode::ELEMENT:

                  {

                      std::cout << "ELEMENT: [" << pNode->ToElement()->Value() << "]" << std::endl;

                      TiXmlAttribute *pAttr = pNode->ToElement()->FirstAttribute();

                      while(pAttr)

                      {

                         std::cout << pAttr->Name() << "=\"" << pAttr->Value() << "\"" << std::endl;

                         pAttr = pAttr->Next();

                      }

                  }

                  break;

 

              case TiXmlNode::TEXT:

                   TiXmlText* pText = pNode->ToText();

                   if(pText)

                       std::cout << "TEXT:  " << pText->Value() << std::endl;

                   break;

              }

 

              for(TiXmlNode *pNext = pNode->FirstChild(); pNext; pNext = pNext->NextSibling())                     ShowXml(pNext);

       }

}

소스

tinyxml.zip

참조:

http://hanburn.tistory.com/7

http://hanburn.tistory.com/8


출처 : http://dolphin.ivyro.net/file/algorithm/etc/tinyXml_read.html

반응형

'Programming > TinyXML' 카테고리의 다른 글

TinyXml 사용법 요약  (0) 2010.12.08
TinyXML Write  (0) 2010.12.08
XML introduce(XML 문법 설명)  (0) 2010.04.27
Tiny XML 링크  (0) 2010.04.27
Posted by blueasa
, |

TinyXML Write

Programming/TinyXML / 2010. 12. 8. 14:36

TinyXML Read의 test.xml과 같은 포맷으로 XML 파일을 저장해 보자.
저장될 파일은 다음과 같다.

test_write.xml

<?xml version="1.0" encoding="euc-kr" ?>

<root>

    <!--각국의 대표 음식들이다.-->

    <food num="1">

        <name>김치</name>

        <country>대한민국</country>

    </food>

</root>

XML 형식 선언

TiXmlDocument doc; 

TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "euc-kr", "" ); 

doc.LinkEndChild( decl );

우리나라 문자셋의 1.0 버전이 만들어 진다.

서브 노드 추가

TiXmlElement * root = new TiXmlElement( "root" ); 

doc.LinkEndChild( root );

"root" 엘리먼트를 추가한다. TinyXML에서 노드를 추가 할 때, new로 생성한 엘리먼트는
내부에서 자동으로 삭제 되기 때문에 해제 할 필요가 없다.

주석 추가

TiXmlComment * comment = new TiXmlComment();

comment->SetValue( "각국의 대표 음식들이다." ); 

root->LinkEndChild( comment );

root의 자식으로 "food" 서브 노드 추가

TiXmlElement *food = new TiXmlElement( "food" ); 

root->LinkEndChild( food );

root의 자식이 되는 "food" 서브 노드를 추가한다.

food의 Attribute로 "num" 추가

food->SetAttribute("num", 1);   //food->SetAttribute("num", "1")와 동일

food->SetAttribute( "num", "1" )도 동일한 결과이다.

food의 자식으로 name 서브 노드와 값 추가

TiXmlElement *name = new TiXmlElement( "name" ); 

food->LinkEndChild( name ); 

name->LinkEndChild( new TiXmlText( "김치" ));

food의 자식 노드로 "name"을 추가하고 값은 "김치"이다.

food의 자식 노드로 country 서브 노드와 값 추가

TiXmlElement *country = new TiXmlElement( "country" ); 

food->LinkEndChild( country ); 

country->LinkEndChild( new TiXmlText( "대한민국" ));

food의 자식 노드로 "country"를 추가하고 값은 "대한민국"이다.

저장 하기

doc.SaveFile("test_write.xml");

SaveFile()을 통해 원하는 파일명으로 값을 저장한다.

전체 소스

void WriteXml()

{

    //형식 선언

    TiXmlDocument doc; 

    TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "euc-kr", "" ); 

    doc.LinkEndChild( decl );

 

    //서브 노드 추가

    TiXmlElement * root = new TiXmlElement( "root" ); 

    doc.LinkEndChild( root ); 

 

    //주석 추가

    TiXmlComment * comment = new TiXmlComment();

    comment->SetValue( "각국의 대표 음식들이다." ); 

    root->LinkEndChild( comment ); 

 

    //root의 자식으로 food 서브 노드 추가

    TiXmlElement *food = new TiXmlElement( "food" ); 

    root->LinkEndChild( food ); 

 

    //food의 Attribute로 "num" 추가

    food->SetAttribute("num", 1);   //food->SetAttribute("num", "1")와 동일

 

    //food의 자식으로 name 서브 노드와 값 추가

    TiXmlElement *name = new TiXmlElement( "name" ); 

    food->LinkEndChild( name ); 

    name->LinkEndChild( new TiXmlText( "김치" ));

 

    //food의 자식으로 country 서브 노드와 값 추가

    TiXmlElement *country = new TiXmlElement( "country" ); 

    food->LinkEndChild( country ); 

    country->LinkEndChild( new TiXmlText( "대한민국" ));

 

    doc.SaveFile("test_write.xml");

}

콘솔 창으로 XML 파일 보여주기

void PrintXml()

{

    TiXmlDocument doc( "test.xml" );

    doc.LoadFile();

 

    //문자열로..

    TiXmlPrinter printer;

    printer.SetStreamPrinting();

    doc.Accept( &printer );

    const char* pStr = printer.CStr();        // char* 를 반환한다.

    printf( pStr );

#ifdef  TIXML_USE_STL

    const std::string str = printer.Str();    // std::string으로 반환한다.

#endif

}

XML 파일을 콘솔 창으로 보여준다...

소스:

tinyxml.zip

참조:

http://hanburn.tistory.com/7

http://hanburn.tistory.com/8


출처 : http://dolphin.ivyro.net/file/algorithm/etc/tinyXml_write.html

http://dolphin.ivyro.net/file/algorithm/etc/tinyXml_write.html

반응형

'Programming > TinyXML' 카테고리의 다른 글

TinyXml 사용법 요약  (0) 2010.12.08
TinyXML Read  (2) 2010.12.08
XML introduce(XML 문법 설명)  (0) 2010.04.27
Tiny XML 링크  (0) 2010.04.27
Posted by blueasa
, |

기본적으로 C#에서는 C++의 디폴트 매개변수 방법을 지원하지 않는 것 같다.

 

void func(int a, int b=1 ){ ... }

 

이런 함수가 있을 떄 디폴트 매개변수를 지원 안하면 같은 소스더라도 2배가 되기 때문에

 

디폴트 매개변수를 지정하는 것이 편할 때가 많았다.

 

하지만  C#에서는 지원을 따로 안하기 때문에 새로운 방법을 써야 한다.

 

public void func(int a){ func(a, 1); }

public void func(int a, int b){ ... }

 

이렇게 디폴트 파라미터가 필요없는 부분의 오버로딩시켜서 새로 호출 시키는 방법을 사용한다.

 

함수에 리턴값이 있을 경우는...

 

public int func(int a){ return func(a, 1); }

public int func(int a, int b){ ... }

반응형
Posted by blueasa
, |

IME 소스 (한글)

Programming/C/C++ / 2010. 11. 26. 21:58



ime 뭉탱이(module)  날읽어(README) ver 0.01

                     안노라군.
                 TEAM TRINITY.
             http://anoa.wo.to 
             kjm37@hanmail.net
//---------------------------------------------------------------------
적은 허접한 맨트와.. 버그있는 소스로 ime 강좌(같지도 않은)를 써서 
초보 IMER (ime + er)분 들을 나락의 길(?)로 빠트렸던 anoa 군입니다.;

아직도 몇몇 군데의 웹페이지들에 그 글들이 있길래. 너무도 죄송한(이라고 
쓰고 쪽팔린 이라고 읽는다.) 마음에.. 최종버전 소스를 정리해서 올립니다.


이번엔 강좌대신 상세한 주석(이라고 쓰고 게으름이라고 읽는다)으로 때웁니다.


          2003. 2. 16.
           class 청소부.  아노아군.

//---------------------------------------------------------------------
 소스
//---------------------------------------------------------------------
 프로젝트  : vc.net (vc 7.0) 
 마지막 업데이트 : 2003 년 2월 17일.
 소스의 기능  : IME 입력 및.. 특수문자(한자)의 목록 얻어내기.
      커서 기능 추가. (멀티라인은 지원하지 않습니다.)
 프로젝트구성 : ime
      +- BASE
       +- Error.h  // 에러 메세지용 헤더
       +- Main.cpp  // WinMain 및 프록시져.
       +- Main.h
       +- stdafx.cpp
       +- stdafx.h  // 프리컴파일헤더
      +- IME
       +- ImeBox.cpp // 각각의 ImeBox.
       +- ImeBox.h
       +- Ime.cpp  // ImeBase
       +- Ime.h
      +- Document
       +- ReadMe.txt // 지금 이문서.

//---------------------------------------------------------------------
 사용방법.
file://---------------------------------------------------------------------

 . 주석을 읽기가 귀찮으신분은 ImeBox.cpp ImeBox.h ime.cpp ime.h 를 그대로
가져다 쓰시고 main.cpp 부분을 참고하셔서 붙이시길 바랍니다.

 . 한국어외의 다른 ime 입력을 보장하지 않습니다.. (수고하세요)
 
 . ime 특유의.. 입력 오류시 띵띵~ 거리는 소리를 없에실 좋은 아이디어가 있
    으신 분은 연락을 주세요.

 . 소스를 편집 및 재배포 할시에는 연락을(홈페이지) 해주세용~♡ 재배포시
  에는 본문서에 하나의 파트를 만드시고.. 소스의 구성이나 편집한 부분.
  아직 해야할부분. 작성자 등을 남겨주세요.


//---------------------------------------------------------------------
 버전(버진 아님. -//-)
file://---------------------------------------------------------------------
v 0.1    kwonil 님의 도움으로.. 조합중이던 문자를 날렸음. - _-)V



출처 : http://blog.naver.com/cpp2angel?Redirect=Log&logNo=100007002652

[출처] IME 소스 (한글)|작성자 아발롱


반응형

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

ifdef, if defined, ifndef, if !defined  (0) 2011.03.04
enum, 보다 나은 enum  (0) 2011.01.13
[C++] 형변환 (Typecasting) - const_cast, reinterpret_cast, static_cast, dynamic_cast  (0) 2010.09.28
GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
Posted by blueasa
, |

Arrays in C++/CLI

Programming/C++/CLI / 2010. 11. 26. 16:00
The article exposes the new array syntax available in C++/CLI for the declaration and use of CLI arrays

Introduction

Managed arrays are allocated on the CLI heap as opposed to native arrays which are allocated on the unmanaged C++ heap, which essentially means that they are typical garbage collected .NET objects. And throughout this article the term array will refer to a managed array, and if at all a native array is mentioned, it will explicitly be termed as a native array. Some of you might have seen my article on using managed arrays with the old MC++ syntax and I am sure a few of you would have let out pained groans at the really peculiar syntax. Well, those people should be delighted to know that C++/CLI supports managed arrays using a far more intuitive syntax than which existed in the older syntax.

Basic features

Here are some of the basic features of managed arrays in C++/CLI :-

  • Syntax resembles that of C++ templates
  • System::Array is automatic base type for all managed arrays
  • Allocated on the CLR heap (means they get Garbage Collected)
  • Rank of the array need not be 1 (arrays with rank 1 are called single dimensional and those with rank >1 are called multi dimensional)
  • Easily supports jagged arrays (unlike in the older syntax, jagged arrays are easier to declare and use)
  • Implicit conversion to and explicit conversion from System::Array BCL class
  • The rank and dimensions of an array object cannot be changed, once an array has been instantiated

Pseudo-template of an array type

The declaration and usage of array types in C++/CLI seems to use an imaginary template type (there is no such template of course, it�s all VC++ compiler magic) :-

 Collapse
namespace stdcli::language
{
    template<typename T, int rank = 1>
        ref class array : System::Array {};
}

array is declared inside the stdcli::language namespace so as to avoid conflicts with existing source code

Single dimensional array usage

Arrays of a reference type

 Collapse
ref class R
{
public:
    void Test1(int x)
    {
        array<String^>^ strarray = gcnew array<String^>(x);
        for(int i=0; i<x; i++)
            strarray[i] = String::Concat("Number ",i.ToString());
        for(int i=0; i<x; i++)
            Console::WriteLine(strarray[i]);
    }
};

The syntax does look different from that used for native arrays; C++ coders who have used templates should find this a lot more intuitive than people who come from a non-C++ background, but eventually they'll get comfortable with it.

Arrays of a value type

 Collapse
ref class R
{
public:
    void Test2(int x)
    {
        array<int>^ strarray = gcnew array<int>(x);
        for(int i=0; i<x; i++)
            strarray[i] = i * 10;
        for(int i=0; i<x; i++)
            Console::WriteLine(strarray[i]);
    }
};

Unlike in the old syntax, array syntax for value types is exactly the same as that for managed types.

Multi dimensional array usage

Multi dimensional arrays are managed arrays that have a rank greater than 1. They are not arrays of arrays (those are jagged arrays).

 Collapse
ref class R
{
public:
    void Test3()
    {
        array<String^,2>^ names = gcnew array<String^,2>(4,2);
        names[0,0] = "John";
        names[1,0] = "Tim";
        names[2,0] = "Nancy";
        names[3,0] = "Anitha";
        for(int i=0; i<4; i++)
            if(i%2==0)
                names[i,1] = "Brown";
            else
                names[i,1] = "Wilson";
        for(int i=0; i<4; i++)
            Console::WriteLine("{0} {1}",names[i,0],names[i,1]);
    }    
};

Jagged arrays

Jagged arrays are non-rectangular, and are actually arrays of arrays. The new template-style array syntax simplifies the declaration and use of jagged arrays, which is a major improvement over the old syntax where jagged arrays had to be artificially simulated by the developer.

 Collapse
ref class R
{
public:
  void Test()
  {
    array<array<int>^>^ arr = gcnew array<array<int>^> (5); 

    for(int i=0, j=10; i<5; i++, j+=10)
    {
      arr[i] = gcnew array<int> (j);
    }

    Console::WriteLine("Rank = {0}; Length = {1}",
      arr->Rank,arr->Length);
    /*
      Output :-
        Rank = 1; Length = 5
    */

    for(int i=0; i<5; i++)
      Console::WriteLine("Rank = {0}; Length = {1}",
        arr[i]->Rank,arr[i]->Length);
    /*
      Output :-
        Rank = 1; Length = 10
        Rank = 1; Length = 20
        Rank = 1; Length = 30
        Rank = 1; Length = 40
        Rank = 1; Length = 50
    */
  }
};

Using a typedef to simplify jagged array usage

 Collapse
typedef array<array<int>^> JaggedInt32Array;
typedef array<array<String^>^> JaggedStringArray;

ref class R
{
public:
    void Test()
    {
        JaggedInt32Array^ intarr = gcnew JaggedInt32Array(10);
        JaggedStringArray^ strarr = gcnew JaggedStringArray(15);        
    }
};

Directly initialize an array

The new syntax allows painless direct initialization of arrays.

 Collapse
ref class R1
{
public:
  void Test()
  {
    //Single dimensional arrays

    array<String^>^ arr1 = gcnew array<String^> {"Nish", "Colin"};
    array<String^>^ arr2 = {"Nish", "Smitha"};
    
    //Multi dimensional arrays

    array<Object^,2> ^ multiobarr = {{"Nish", 100}, {"Jambo", 200}};
  }
};

Arrays as function arguments

 Collapse
ref class R
{
public:
  void ShowStringArray(array<String^>^ strarr)
  {
    for(int i=0; i<strarr->Length; i++)
      Console::WriteLine(strarr[i]);
  }
  void Show2DInt32Array(array<int,2>^ arr)
  {    
    for(int i=0; i<arr->GetLength(0); i++)
    {
      Console::WriteLine("{0} times 2 is {1}",arr[i,0],arr[i,1]);
    }
  }
};

void _tmain()
{
  R^ r = gcnew R();
  r->ShowStringArray(gcnew array<String^> {"hello", "world"});
  array<int,2>^ arr = { {1,2}, {2,4}, {3,6}, {4,8} };
  r->Show2DInt32Array(arr);
}

//Output :-


/*
  hello
  world
  1 times 2 is 2
  2 times 2 is 4
  3 times 2 is 6
  4 times 2 is 8
*/

Returning arrays from functions

 Collapse
ref class R
{
public:
    array<String^>^ GetNames(int count)
    {
        array<String^>^ arr = gcnew array<String^>(count);
        for(int i=0; i<count; i++)
        {
            arr[i] = String::Concat("Mr. ",(i+1).ToString());
        }
        return arr;
    }

    void ShowNames(array<String^>^ arr)
    {
        for(int i=0; i<arr->Length; i++)
            Console::WriteLine(arr[i]);
    }
};

void _tmain()
{
    R^ r = gcnew R();
    array<String^>^ arr = r->GetNames(7);
    r->ShowNames(arr);
}

Array covariance

You can assign an array of type R to an array of type B, where B is a direct or indirect parent of R, and R is a ref class.

 Collapse
ref class R1
{
    //...

};

ref class R2 : R1
{
    //...

};

void _tmain()
{
    array<R1^>^ arr1 = gcnew array<R1^>(4);
    array<R2^>^ arr2 = gcnew array<R2^>(4);    
    
    array<R1^>^ arr3 = arr2;    
    array<R1^>^ arr4 = gcnew array<R2^>(4);    
}

Parameter arrays

C++/CLI has support for parameter arrays. There can only be one such parameter array per function and it also needs to be the last parameter.

 Collapse
ref class R
{
public:
    void Test(String^ s, [ParamArray] array<int>^ arr )    
    {
        Console::Write(s);
        for(int i=0; i<arr->Length; i++)
            Console::Write(" {0}",arr[i]);
        Console::WriteLine();
    }
};

void _tmain()
{
    R^ r = gcnew R();
    r->Test("Nish");
    r->Test("Nish",1);
    r->Test("Nish",1,15);
    r->Test("Nish",1,25,100);
}

Right now the only supported syntax uses the ParamArray attribute, but the eventual syntax will also support the simpler style shown below :-

  • void Test(String^ s, ... array<int>^ arr )

Calling System::Array methods

Since every C++/CLI array is implicitly a System::Array object, we can use System::Arraymethods on CLI arrays.

 Collapse
ref class R
{
public:
  bool CheckName(array<String^>^ strarr, String^ str)
  {
    Array::Sort(strarr);
    return Array::BinarySearch(strarr,str) < 0 ? false : true;
  }
};

void _tmain()
{
  R^ r = gcnew R();
  array<String^>^ strarr = {"Nish","Smitha",
    "Colin","Jambo","Kannan","David","Roger"};  
  Console::WriteLine("Nish is {0}",r->CheckName(strarr,"Nish") ? 
    gcnew String("Present") : gcnew String("Absent"));
  Console::WriteLine("John is {0}",r->CheckName(strarr,"John") ? 
    gcnew String("Present") : gcnew String("Absent"));
}

I've used System::Sort and System::BinarySearch in the above example.

Here's another snippet that clearly demonstrates the implicit conversion to System::Array.

 Collapse
ref class R
{
public:
    void ShowRank(Array^ a)
    {
        Console::WriteLine("Rank is {0}",a->Rank);
    }
};

void _tmain()
{
    R^ r = gcnew R();    
    r->ShowRank( gcnew array<int>(10) );
    r->ShowRank( gcnew array<String^,2>(10,2) );
    r->ShowRank( gcnew array<double,3>(10,3,2) );    
    r->ShowRank( gcnew array<int> {1,2,3} );
    r->ShowRank( gcnew array<int,2> {{1,2}, {2,3}, {3,4}} );
}

Arrays of non-CLI objects

You can declare C++/CLI arrays where the array type is of a non-CLI object. The only inhibition is that the type needs to be a pointer type. Consider the following native C++ class :-

 Collapse
#define Show(x) Console::WriteLine(x)

class N
{
public:
   N()
   {
      Show("N::ctor");
   }
   ~N()
   {
      Show("N::dtor");
   }
};

Now here's how you can declare an array of this type :-

 Collapse
ref class R
{
public:   
   void Test()
   {
      array<N*>^ arr = gcnew array<N*>(3);
      for(int i=0; i<arr->Length; i++)   
         arr[i] = new N();
   }
};

Put this class to use with the following test code :-

 Collapse
void _tmain()
{
   R^ r = gcnew R();
   r->Test();
   Show("Press any key...");
   Console::ReadKey();
}

/* Output:

N::ctor
N::ctor
N::ctor
Press any key...

*/

There, that worked. Of course the destructors for the array elements did not get called, and in fact they won't get called even if a Garbage Collection takes place and the array object is cleaned up. Since they are native objects on the standard C++ heap, they need to have delete called on them individually.

 Collapse
ref class R
{
public:   
   void Test()
   {
      array<N*>^ arr = gcnew array<N*>(3);
      for(int i=0; i<arr->Length; i++)   
         arr[i] = new N();
      for(int i=0; i<arr->Length; i++)   
         delete arr[i];
   }
   //...


/* Output

N::ctor
N::ctor
N::ctor
N::dtor
N::dtor
N::dtor
Press any key...

*/

Ok, that's much better now. Or if you want to avoid calling delete on each object, you can alternatively do something like this :-

 Collapse
ref class R
{
public:   
   void Test()
   {
      N narr[3];
      array<N*>^ arr = gcnew array<N*>(3);
      for(int i=0; i<arr->Length; i++)
         arr[i] = &narr[i];   
   }   
};

This yields similar results to the above snippet. Alternatively you could init the array members in its containing class's constructor and delete them in the destructor, and then use the containing class as an automatic variable (C++/CLI supports deterministic destruction).

Direct manipulation of CLI arrays using native pointers

Here's some code that shows how you can directly manipulate the contents of an array using native pointers. The first sample is for a single dimensional array and the second is for a jagged array.

Natively accessing a single-dimensional array

 Collapse
void Test1()    
{
    array<int>^ arr = gcnew array<int>(3);
    arr[0] = 100;
    arr[1] = 200;
    arr[2] = 300;

    Console::WriteLine(arr[0]);
    Console::WriteLine(arr[1]);
    Console::WriteLine(arr[2]);

    /*
    Output :-
      100
      200
      300
    */

    // Modifying the array using a native int* 

    // that points to a pinned pointer in GC'd heap

    pin_ptr<int> p1 = &arr[0];
    int* p2 = p1;
    while(*p2)
    {           
        (*p2)++;
        p2++;
    }

    Console::WriteLine(arr[0]);
    Console::WriteLine(arr[1]);
    Console::WriteLine(arr[2]);

    /*
    Output :-
      101
      201
      301
    */
} 

Natively accessing a jagged array

 Collapse
void Test2()
{
    array<array<int>^>^ arr = gcnew array<array<int>^>(2);
    arr[0] = gcnew array<int>(2);
    arr[1] = gcnew array<int>(2);
    arr[0][0] = 10;
    arr[0][1] = 100;
    arr[1][0] = 20;
    arr[1][1] = 200;

    Console::WriteLine(arr[0][0]);
    Console::WriteLine(arr[0][1]);
    Console::WriteLine(arr[1][0]);
    Console::WriteLine(arr[1][1]);

    /*
    Output:
      10
      100
      20
      200
    */


    // Copying the managed jagged array to

    // a native array of pointers and accessing

    // the members using the native array

    pin_ptr<int> p1 = &arr[0][0];
    pin_ptr<int> p2 = &arr[1][0];
    int* p3[2];
    p3[0] = p1;
    p3[1] = p2;

    Console::WriteLine(p3[0][0]);
    Console::WriteLine(p3[0][1]);
    Console::WriteLine(p3[1][0]);
    Console::WriteLine(p3[1][1]);

    /*
    Output:
      10
      100
      20
      200
    */

    // Assigning the native array of pointers

    // to a native int** and modifying the array

    int** p4 = p3;
    for(int i=0; i<2; i++)
        for(int j=0; j<2; j++)
            p4[i][j] += 3;          

    Console::WriteLine(arr[0][0]);
    Console::WriteLine(arr[0][1]);
    Console::WriteLine(arr[1][0]);
    Console::WriteLine(arr[1][1]);

    /*
    Output:
      13
      103
      23
      203
    */
}

Essentially we use a pinning pointer to the GC'd heap and then treat the casted native pointer as if it were pointing to a native array. Gives us a really fast method to manipulate array content!

Conclusion

Array syntax in C++/CLI is definitely an aesthetic improvement over the older MC++ syntax, and it also brings in a consistency of syntax that was severely lacking earlier. The template-style syntax should give a natural feel for C++ coders though it might take a little while before you fully get used to it. As usual, I request you to freely post any comments, suggestions, criticism, praise etc. that you might have for me.


출처 : http://www.codeproject.com/KB/mcpp/cppcliarrays.aspx

반응형
Posted by blueasa
, |
부모 폼과 자식 폼이 있다; 자식 폼에서 A라는 버튼을 클릭했을 때; B라는 텍스트 박스의 내용을 부모 폼의 C라는 텍스트 박스에 표시하고 싶다; 그 방법은?

물론; 가장 간단한 방법은 private으로 선언되어 있는 C라는 텍트스 박스를 public으로 선언하고; 자식 폼에서 직접 이 텍스트 박스에 접근하여; 값을 대입하는 방법이 있습니다;

하지만; 개인적으로 이 방법은 C#이 지향하는 OOP(Object-oriented paradigm)를 위배하고; 깔끔하지 못한 방법으로 별로 권장하고 싶지 않네요;

개인적으로 권장하는 것은 이벤트(event)를 이용하는 것 입니다; 이벤트는 단순히 폼 간의 어떤 데이터를 주고 받을 수 있을 뿐만 아니라; 서로 다른 클래스 간의 데이터 전송에도 유용하며, 데이터를 구조체 형식으로도 넘길 수 있는 등 다양한 장점이 있지요; 무엇보다, OOP를 준수하면서도; 깔끔한 코드를 유지할 수 있다는 것에 후한 점수를 주고 싶군요;

이 이벤트를 구현하는 방법에 가장 핵심이 되는 내용은 바로 delegate 입니다.
C++/MFC, java 등 다른 언어에 능숙한 사람들 대부분이 C#에 금방 적응하게 됨에도 불구하고; 그나마 C#에서 가장 당황하는 개념이 delegate가 아닐까 싶네요; (나만 그런가?;)

delegate는 한글로 굳이 번역하자면; 대리자라고 하는데; C#이 다른 언어와 차별성을 가지는 중요한 몇 몇 이슈 중 하나가 이 녀석의 존재라고 생각합니다;

단어 뜻으로만 이해하자면; 어려우니까; 서두에 언급한 자식 폼에서 부모 폼으로 텍스트를 전달하는 예제를 통해; delegate의 역할을 이해해보죠;

이미 언급했지만; 자식 폼에서 부모 폼으로 데이터를 전달할 때, 이벤트를 이용하기로 하였는데; 이벤트에서 가장 기본적인 사실은; 이벤트를 발생하는 곳이 있으면, 이벤트를 받는 쪽도 있어야 된다는 것입니다;

따라서, 자식 폼에는 이벤트를 발생시키는 녀석을 정의하고, 부모 폼에서는 이벤트를 받는 녀석을 정의해야 된겠지요. 이미 C#을  이용해 윈 폼 프로그래밍을 해본 사람이라면; 폼에 버튼을 붙이고; 클릭 이벤트를 추가해서; 버튼을 클릭하면; 메시지 박스로 "Hello, World!" 정도는 경험이 있을 것 입니다;

이 때, 버튼도 우리가 예로 들려는 자식 폼과 같습니다; 버튼이 이벤트를 발생시키고, 부모 폼에서 버튼 이벤트를 받아, 거기서 메시지 박스를 띄운 것이기 때문이지요;

자 이벤트를 정의하는 과정은 다음과 같습니다;
사용자 삽입 이미지

클래스 ChildFormEventArgs는 이벤트 데이터가 들어 있는 클래스에 대한 기본 클래스인 EventArgs를 상속 받아 구현하였습니다. 우리는 자식 폼에서 부모 폼으로 텍스트를 전송할 목적이므로, 기본 클래스에 message란 속성을 추가하였구요;

그리고 그 아래 이벤트 대리자(delegate)를 선언하는데, 대리자를 통해 넘기는 파라미터에 주목해봅시다;
ChildFormEventArgs는 앞에 언급한대로 이벤트 데이터 이고, object sender는 뭘까요?

이벤트를 발생 시킨 주체입니다; 우리의 예에서는 바로 자식폼이 되겠지요;

이벤트를 위한 대리자를 선언했으면; 자식 폼에 이 이벤트를 발생시키기 위한 코드를 구현해야 합니다;
사용자 삽입 이미지

이벤트 선언부를 눈여겨 보세요;

이에 앞서 선언한 delegate에 이벤트를 위한 엑세스 한정자인 event를 붙였습니다; 그리고 파생 클래스에서 이 이벤트를 일으키거나 override 할 수 있도록 protected와 virtual 키워드를 붙였지요;

그리고 그 아래 정의된 메소드는 자식 폼에 추가한 버튼을 클릭하면 호출되는 녀석; 이미 알고 있듯이; 우리는 버튼을 클릭했을 때, 자식 폼에 있는 텍스트 박스(txtSentMessage)에 적힌 텍스트를 부모 폼에 전달하기 위함이므로; 앞서 정의한 event를 발생시킬 수 있는 NotifyEvent를 호출하도록 하였습니다;

그럼, 이제 부모 폼에서 이벤트를 받을 수 있도록 구현해보죠;
사용자 삽입 이미지

부모 폼의 생성자(constructor)에 자식폼의 메모리를 할당하고; 이벤트를 받기 위해 OnNotifyParent이벤트 핸들러를 추가합니다;

자, 여기서 대리자(delegate)의 역할을 이해할 수 있습니다. 자식 폼에서는 결코 부모 폼의 child_OnNotifyParent 메소드를 직접적으로 호출하지 않습니다. 단지, 이벤트로 정의한 OnNotifyParent(delegate)를 구현하였고; 이를 이용해 이벤트를 발생시키는 NotifyEvent 메소드만 구현했을 뿐이지요; 하지만 OnNotifyParent는 대리자란 명칭에 걸맞게; NotifyEvent가 호출되면; 간접적으로 child_OnNotifyParent 해줬습니다;

이 때 이벤트 데이터를 전달해주는 ChildFormEventArgs를 통해 자식폼의 텍스트 박스에 적힌 텍스트를 가져올 수 있네요;

다시, 정리하자면 이렇습니다.
1. 이벤트는 이벤트를 발생시키는 곳이 있으면, 발생한 이벤트를 듣는 곳도 있다;
2. 이벤트는 이벤트를 발생시키는 곳과 발생한 이벤트를 듣는 곳을 잇기 위한 대리자(delegate)로 구현한다.

delegate란 개념은 단지; 이벤트를 구현할 때만 쓰이지 않습니다; 제가 오래 전에 블로그에 쓴 C#에서 크로스 쓰레드 문제에서도 언급된 적이 있습니다. 사실 저도 저 때 delegate 개념이 구체화 되지는 않았습니다; 어렴풋이 이해만 했었고; 단지 저런 상황이 생기면; 다시 써먹기 위해 남겼을 뿐이지요..;

혹시나 필요하신 분들이 있을까; 아티클에 설명한 예제의 소스 코드를 첨부합니다;



반응형
Posted by blueasa
, |
C++을 사용하시는 분들은 VC계열에서 전처리자을 이용하는 방법에 대해서 익숙하신 분들이 많으실겁니다. 
특히 전처리자, #define, #if ~ #endif등을 이용해서 디버그할때만 필요한 코드를 릴리즈할때 무시하도록 하는 일은 한번쯤은 해보셨지 않았나 싶습니다. 

C#으로 넘어오면서 네임스페이스등의 개념이 본격적으로 사용되고 전처리자를 잘 사용하지 않게 되었습니다. 하지만 코드를 짜다보니 여전히 디버그용으로 만든 코드가 릴리즈에 포함되지 않게 하는일이 필요하더군요. 그래서 어떻게 쓸수있지 않을까 고민하던 중에 다음과 같은방법을 발견했습니다. 

using System; 
using System.Diagnostics; 

class Program { 
    static void Main(string[] args) { 

        #if DEBUG 
        DebugMethod1(); 
        #endif 

        DebugMethod2(); 
        Console.ReadKey(); 
    } 
    static void DebugMethod1() { 
        Console.WriteLine("Debug1"); 
    } 

    [Conditional("DEBUG")] 
    static void DebugMethod2() { 
        Console.WriteLine("Debug2"); 
    } 
}

from : Eric Gunnerson's C# Compendium
         http://blogs.msdn.com/ericgu/archive/2004/08/13/214175.aspx


빨갛게 된 부분을 주목해주세요, 위의 전처리자를 이용한 방법은 코드라인 일부를 디버그 모드에서 활성화하기 위해 감싸는 것입니다. VS에서 릴리즈 모드 컴파일로 들어가면 위 전처리자 사이의 코드는 회색으로 변하면서 실행이 안됩니다. 아래 Attribute를 사용하는 방법은 메소드 전체를 디버그모드에서만 컴파일 하기 위해 사용하는 것입니다. 이러한 팁은 매우 간단하지만 많은 분들이 모르시더라구요... 역시 구글검색은 안나오는게 없는것 같습니다. ^^

 좀더 덧붙이자면, C#에는 전처리 과정이 없기 때문에 컴파일 과정에서 무시되는것 같습니다. 그리고 뒤의 Attribute를 사용하는 방법은 문제가 있는데 함수의 return값이 void 이어야만 하고 public 메소드를 저렇게 만들게 되면 나중에 잡기힘든 버그가 발생 (분명 실행시켰는데 실행조차 안되는) 한답니다. 참고하세요.


반응형
Posted by blueasa
, |
툴작업을 하다보면 Native C++에서 Managed C++에 접근해야 할 필요가 있다.
정확히 말해서 두 영역간의 소통이 필요할때가 있다.

예로 현재 작업 중인 툴에서 C#에 만들어진 Log 폼 다이얼로그에 로그를 찍고 싶을 때, Native C++에서 어떻게 작업을 해야할까?
Native C++에서는 Managed C++ 코드를 사용하거나 함수를 호출할수도 없다.
해결 방법 중 하나는 클래스 상속과 다형성을 이용하는 방법이다.

먼저 Native C++에서 밑의 코드와 같이 정의해준다

// 로그 클래스
class CLog
{
public:
        virtual void Print( const char* str ) = 0;
};

// 로그를 사용할 클래스
class CFramework
{
public:
        void SetLogger( ae_CLog* log ) { m_pLogger = log; }
private:
        CLog* m_pLogger;
}

그 다음 랩핑 클래스에서 위에서 정의한 CLog 클래스를 상속 받아 자식 클래스를 하나 만든다

public class ManagedLogger : public CLog
{
public:
        virtual void Print( const char* str )
        {
                // 이곳에 C#에서 수행할 작업을 코딩한다.
                // 이곳은 Unamanaged 영역이므로, 바로 Managed 코드를 못 쓴다.
                // 그러니 Managed 클래스에서 Static 함수를 만들어 여기서 호출해준다
                // 예) CFrameworkWrapper::WriteLog( str );
        }
};

주의할점은 주석에 적어놓았다. Managed 코드와 UnManaged 코드는 혼용될수 없으므로, 오로지 함수로만 통신을 해야한다.
자식 클래스를 정의 하였으면, 이제 이것을 사용해보자.

void CFrameworkWrapper::Create()
{
        m_pFrameWork = new CFramework();
        m_pFrameWork->SetLogger( new ManagedLogger() );
}

Native C++의 CFramework를 선언해주고, SetLogger 함수에 ManagedLogger 클래스의 객체 포인터를 넘겨주고 있다. ManagedLogger는 CLog 클래스를 상속받은 클래스 이므로, CLog 클래스로도 ManagedLogger 클래스를 가리킬수 있다. 이젠, 가상함수를 통해 Native C++ 영역에서 ManagedLogger의 함수를 호출할수 있는 것이다.


반응형
Posted by blueasa
, |

You want to use Dictionary in your C# program for constant lookup times and to associate keys with values. Look at some examples of using Dictionary with Keys and KeyValuePair, as well as with classes and methods. This document has tips and examples for using Dictionary with keys and values using the C# programming language.

Dictionary provides fast lookup of elements.
Used when you have many different elements.
Found in the System.Collections.Generic namespace.
Specify the types of its keys and values.

Adding keys

To get started, let's add four keys with values to a Dictionary instance. Afterwards, we look inside the Dictionary using Visual Studio's debugger. You will see that the Dictionary is composed of separate keys and values. Here's the Dictionary code and then its internal view.

~~~ Program that uses Dictionary Add method (C#) ~~~

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> d = new Dictionary<string, int>();
        d.Add("cat", 2);
        d.Add("dog", 1);
        d.Add("llama", 0);
        d.Add("iguana", -1);
    }
}

Inside the Dictionary. Here's what the above code looks like in memory. It is represented by a collection of key and value pairs. The screenshot is worth looking at. This article is based on .NET 3.5 SP1, but the contents apply to other versions as well.

Dictionary internals

Looking up values

Here we see how you can check to see if a given string is present in a Dictionary with string keys. We look at more types of Dictionaries further on, but here is the ContainsKey method.

=== Program that uses ContainsKey (C#) ===

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> d = new Dictionary<string, int>();
        d.Add("apple", 1);
        d.Add("windows", 5);

        // See if Dictionary contains this string
        if (d.ContainsKey("apple")) // True
        {
            int v = d["apple"];
            Console.WriteLine(v);
        }

        // See if Dictionary contains this string
        if (d.ContainsKey("acorn"))
        {
            Console.WriteLine(false); // Nope
        }
    }
}

=== Output of the program ===

1

Note on efficiency. There is a more efficient method called TryGetValue on the Dictionary class, and you should definitely use it when possible. As its name implies, it tests for the key and then returns the value if it finds the key.

See TryGetValue Method.

KeyNotFoundException

If you are running into the KeyNotFoundException, you are accessing a key in your Dictionary that doesn't exist. Dictionary is not the same as Hashtable and you must test keys for existence first, with ContainsKey or TryGetValue.

See KeyNotFoundException Fix.

Understanding KeyValuePair

Hint: this is not in every beginner's C# book. When Dictionary, or any object that implements IDictionary, is used in a foreach loop, it returns an enumeration. In the case of Dictionary, this enumeration is in the form of KeyValuePairs.

See KeyValuePair Collection Hints.

Using foreach on Dictionary

Here we use foreach syntax and KeyValuePair generics in the foreach loop. With collections like Dictionary, we must always know the value types. With each KeyValuePair, there is a Key member and Value member.

=== Program that uses foreach on Dictionary (C#) ===

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Example Dictionary again
        Dictionary<string, int> d = new Dictionary<string, int>()
        {
            {"cat", 2},
            {"dog", 1},
            {"llama", 0},
            {"iguana", -1}
        };
        // Loop over pairs with foreach
        foreach (KeyValuePair<string, int> pair in d)
        {
            Console.WriteLine("{0}, {1}",
                pair.Key,
                pair.Value);
        }
        // Use var keyword to enumerate dictionary
        foreach (var pair in d)
        {
            Console.WriteLine("{0}, {1}",
                pair.Key,
                pair.Value);
        }
    }
}

=== Output of the program ===

cat, 2
dog, 1
llama, 0
iguana, -1

cat, 2
dog, 1
llama, 0
iguana, -1

Overview of the code. The code example declares and populates an example Dictionary. This Dictionary happens to indicate what animals we have and how many of them.

Using the foreach loop. It has a ShowDictionaryPair method. This method demonstrates the foreach loop and the KeyValuePair declaration. Pay careful attention to the syntax in the foreach loop. Each KeyValuePair has two members, pair.Key and pair.Value, which contain string keys and int values.

Using the var keyword. The final loop in the code shows how you can make the syntax for looping really simple by using the var keyword. This is not always desirable on some projects, however.

Getting all Dictionary keys

Here we use the Keys property and then look through each key and lookup the values. This method is slower but has the same results. Using the Keys collection and putting it in an array or List is very effective in other situations.

=== Program that gets Keys from Dictionary (C#) ===

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> d = new Dictionary<string, int>()
        {
            {"cat", 2},
            {"dog", 1},
            {"llama", 0},
            {"iguana", -1}
        };
        // Store keys in a List
        List<string> list = new List<string>(d.Keys);
        // Loop through list
        foreach (string k in list)
        {
            Console.WriteLine("{0}, {1}",
                k,
                d[k]);
        }
    }
}

=== Output of the program ===

cat, 2
dog, 1
llama, 0
iguana, -1

Benchmarking KeyValuePair usage

Using foreach on KeyValuePairs is several times faster than using Keys. This is probably because the Keys collection is not used. KeyValuePair allows us to simply look through each pair one at a time. This avoids lookups and using the garbage-collected heap for storage.

See Garbage Collection Visualizations.

=== Benchmark for KeyValuePair foreach loop ===

KeyValuePair: 125 ms
Note:         This loops through the pairs in the Dictionary.

Keys loop:    437 ms
Note:         This gets the Keys, then loops through them.
              It does another lookup for the value.

Note on the benchmark. The author made a small change to the Keys version for clarity and performance, so these figures are only general and apply to the previous version. Using KeyValuePair is most likely still faster.

Sorting your Dictionary values

If you need to sort the values in your Dictionary, you may be perplexed at first and wonder how to order the keys properly. Fortunately, I have an article about how to do this, although it is not optimal.

See Sort Dictionary Values.

Related collections

You will find other collections, such as SortedDictionary, in the base class libraries, BCL, available for you to use. However, my experience is that it is hard to get as good performance as with Dictionary.

See SortedDictionary.

Using different types in Dictionary

Dictionary in C# is a generic class, which means it requires you to specify a type for it to use. So, you can use an int key, just as easily as a string key. Here is an example of a Dictionary with int keys.

=== Program that uses int keys (C#) ===

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // Use a dictionary with an int key.
        Dictionary<int, string> dict = new Dictionary<int, string>();
        dict.Add(100, "Bill");
        dict.Add(200, "Steve");
        // You can lookup the int in the dictionary.
        if (dict.ContainsKey(200))
        {
            Console.WriteLine(true);
        }
    }
}

=== Output of the program ===

True

Advanced features of Dictionary. For more advanced developers, you can use the GetHashCode() method and override it to create Dictionaries or hashes with the class. This can improve performance in those cases.

Enhancing lookup speed

To enhance lookup speed on your Dictionary, you can change the size of the keys you use. My research has shown that when you use shorter string keys, the lookup time is improved. This could create more collisions, so testing may be necessary.

Using Dictionary with LINQ

By using the ToDictionary method, which is an extension method on IEnumerable that will place the keys and values into a new Dictionary using lambda expressions.

=== Program that uses LINQ (C#) ===

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        string[] arr = new string[]
        {
            "One",
            "Two"
        };
        var dict = arr.ToDictionary(item => item, item => true);
        foreach (var pair in dict)
        {
            Console.WriteLine("{0}, {1}",
                pair.Key,
                pair.Value);
        }
    }
}

=== Output of the program ===

One, True
Two, True

Explanation of the example. The above example uses ToDictionary, which resides in the System.Linq namespace, on the string[] array to create a lookup table where both strings can be accessed in constant time, O(1).

Using ContainsValue to find values

Dictionary also helpfully implements a method called ContainsValue. This method does not enjoy the constant-time lookup speed that ContainsKey has, however. It instead searches the entire collection, making it linear in complexity.

=== Program that uses ContainsValue (C#) ===

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> d = new Dictionary<string, int>();
        d.Add("cat", 1);
        d.Add("dog", 2);
        if (d.ContainsValue(1))
        {
            Console.WriteLine(true); // true
        }
    }
}

=== Output of the program ===

True

ContainsValue method. The above example will internally loop through all elements in the Dictionary until it finds the match, or there are no more to check. MSDN: "This method is an O(n) operation, where n is Count."

See ContainsValue Dictionary Example.

Clearing and counting

You can erase all the key/value pairs within your Dictionary by using the Clear() method, which accepts no parameters. Alternatively, you can assign the variable to null. The difference between Clear and null is not important for memory, as in either case the entries are garbage-collected. Internally, I see that Clear calls Array.Clear, which is not managed code.

Counting your Dictionary. The Count method on the Dictionary collection gives you an effective way of computing the total number of keys in the instance. This is much simpler than accessing the Keys property or looping over the Dictionary to count it. This Count property is covered in more detail on this site.

See Count Dictionary, Using Count Property.

Removing an entry

Here you want to eliminate an entry, not just by setting its value to null or string.Empty, but by also removing the key itself. Fortunately, you can use the Remove method.

~~~ Program that uses Remove (C#) ~~~

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Dictionary<string, int> d = new Dictionary<string, int>();
        d.Add("cat", 1);
        d.Add("dog", 2);

        d.Remove("cat");     // Removes cat
        d.Remove("nothing"); // Doesn't remove anything
    }
}

Execution of the code. Running the code in Visual Studio, no exceptions are thrown, which means that when you remove a key that doesn't exist, nothing happens. However, Remove throws System.ArgumentNullException when it receives a null parameter.

Copying entire Dictionary

The Dictionary class has a useful constructor that allows you to easily copy all the values and keys in your Dictionary into a new Dictionary instance. You can write the logic yourself, but using this constructor improves code reuse and simplicity. This site has more information on the Dictionary copy constructor.

See Copy Dictionary.

Parameters and return values

It is also possible to use the Dictionary constructed type in the C# language as a parameter to methods or as a return value from methods or properties. Because the Dictionary type is defined as a class, it is always passed as a reference type, meaning only 32-64 bits will be copied on the method invocation. The same principles apply when copying a Dictionary return value from a method. You can find more information on Dictionary parameters on this site.

See Dictionary Parameter and Return Value.

Question

Should I use List or Dictionary?

I suggest you almost always use Dictionary when you need to do lookups. If you use List and you need to look up a key, your program may freeze if you happen to have a huge number of elements. In other words, if you use Dictionary, your program can recover from pathological, edge cases.

Using multiple variables in single key

You can sometimes use multiple variables in a key by creating a special function that transforms those variables into a string, serializing them. So, you could use the string "1,2" to mean the ints 1 and 2.

Initializing Dictionary at class level

Sometimes it is useful to have a Dictionary in your class that is allocated at the class level, not in a method or constructor. Additionally, if you have a static class then you should always initialize your Dictionary at the class level like this instead of the static constructor. Static constructors have performance penalties, which I have measured.

=== Program that uses Dictionary with class (C#) ===

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Example e = new Example();
        Console.WriteLine(e.GetValue());
    }
}

class Example
{
    Dictionary<int, int> _d = new Dictionary<int, int>()
    {
        {1, 1},
        {2, 3}, // <--
        {3, 5},
        {6, 10}
    };
    public int GetValue()
    {
        return _d[2]; // Example only
    }
}

=== Output of the program ===

3

Summary

In this article, we saw how you can use Dictionary with KeyValuePair to look through all pairs in the Dictionary. Additionally, we saw material on ContainsKey, which lets you check key existence. Finally, the author considers Dictionary one of the most interesting subjects in the programming world.

See Dictionary Articles.

© 2007-2010 Sam Allen. All rights reserved.


출처 : http://dotnetperls.com/dictionary-keys

반응형
Posted by blueasa
, |
지난번에 Thread를 왜 쓰레드라고 표기한지에 대해서와 단일 쓰레드의 시작과 종료에 대해 썰을 풀었다.
이번에는 멀티 쓰레드와 동기화를 함해보고 담에는 쓰레드 풀에 대해 썰을 풀어보자.

using System;
using System.Threading; 

//데이터를 주고받아야 하니 Class를 하나 만들고 인터페이스도 만들어 두자.
class work{
  int a; //받을 인자값

  //인터페이스 메소드
  public work(int a){
   this.a = a;
  }
  
  //실제 일할놈
  public void runit(){
   for (int i=0; i<10; i++){
    Console.WriteLine("Thread{0} Running : {1}", a, i);
    Thread.Sleep(100);
   }
  }
}

//메인쓰레드가 있는 클래스
class Test {
  static void Main(){ 
   Console.WriteLine("쓰레드 시작");
   work wk1 = new work(1); //1로 지정
   work wk2 = new work(2); //2로 지정
   ThreadStart td1 = new ThreadStart(wk1.runit); //시작쓰레드 선언하고
   ThreadStart td2 = new ThreadStart(wk2.runit);
   Thread t1 = new Thread(td1); //돌릴준비하고
   Thread t2 = new Thread(td2);
   t1.Start(); //돌리자
   t2.Start();
  }
}

사용자 삽입 이미지
실행후 1번과 2번 쓰레드가 동시에 작동한다.

그런데 1번보다 2번 쓰레드가 중요하다던가 하는 상황에서는 어떻게 하면 될까?
그때는 "ThreadPriority"메소드를 사용하여 우선순위를 지정할 수 있다.

using System;
using System.Threading; 
class work{
  int a;
  public work(int a){
   this.a = a;
  }
  
  public void runit(){
   for (int i=0; i<10; i++){
    Console.WriteLine("Thread{0} Running : {1}", a, i);
    Thread.Sleep(100);
   }
  }
}
class Test {
  static void Main(){ 
   Console.WriteLine("쓰레드 시작");
   work wk1 = new work(1);
   work wk2 = new work(2);
   ThreadStart td1 = new ThreadStart(wk1.runit);
   ThreadStart td2 = new ThreadStart(wk2.runit);
   Thread t1 = new Thread(td1);
   Thread t2 = new Thread(td2);
   t1.Priority = ThreadPriority.Lowest; //이 부분 추가됨. 1번 쓰레드 우선 순위 최하
   t2.Priority = ThreadPriority.Highest; //이 부분 추가됨. 2번 쓰레드 우선 순위 최고
   t1.Start();
   t2.Start();
  }
}

1번 쓰레드에 우선 순위를 최하로 부여하고 2번 쓰레드에는 우선 순위를 올려 보았다.
(설정은 "Highest, AboveNormal, Normal, BelowNormal, Lowest"로 5단계로 설정할 수 있다.)
사용자 삽입 이미지
본 코드가 적용되기전의 결과와는 다르게 2번 쓰레드가 먼저 생성되고 종료된다.
그러나 믿지는 말자. 인텔 계열의 CPU는 0부터 31까지의 값을 가지고 있고 이는 윈도우에서 사용하는 우선 순위나 별반 다르지 않다.

* 작업관리자에서 우선순위를 설정해 보신분들은 금방 알아볼 것이다. 다음의 그림처럼...
사용자 삽입 이미지
암튼 지맘이니까 믿지는 말자. 쓰레드의 위험성은 이처럼 결과를 예측하고 그 예측이 맞을것이라 바라는것 되겠다. 절대 하지말아야 할 주의점 이다.

이렇게 멀티로 작업을 하다보면 공통 변수로 작업해야 할때가 있다. 이때 여러개의 쓰레드가 1개의 값을 건드리다보면 변수의 값이 엉뚱하게 나오기도 한다.

using System;
using System.Threading; 
class work{
  public int a;
  public void runit(){
   int tp = a + 1;
   Console.WriteLine(tp.ToString());
   Thread.Sleep(10);
   a = tp;   
  }
}
class Test {
  static void Main(){ 
   Console.WriteLine("쓰레드 시작");
   work wk = new work();
   Thread[] td = new Thread[5];
   for (int i=0; i<5; i++){
   td[i] = new Thread(new ThreadStart(wk.runit));
   td[i].Start();
   }
   Thread.Sleep(1000);
   Console.WriteLine("최종값:{0}",wk.a);
  }  
}

요녀석을 실행함 해보자.
사용자 삽입 이미지
결과값은 분명 5가 나와야 하지만 그렇지 않고 1로 고정이 되어버렸다. 
다음과 같이 lock이란 녀석을 한번 넣어보자.
using System;
using System.Threading; 
class work{
  public int a;
  public void runit(){
   lock(this){ //바로 여기
   int tp = a + 1;
   Console.WriteLine(tp.ToString());
   Thread.Sleep(10);
   a = tp;   
    }
  }
}
class Test {
  static void Main(){ 
   Console.WriteLine("쓰레드 시작");
   work wk = new work();
   Thread[] td = new Thread[5];
   for (int i=0; i<5; i++){
   td[i] = new Thread(new ThreadStart(wk.runit));
   td[i].Start();
   }
   Thread.Sleep(1000);
   Console.WriteLine("최종값:{0}",wk.a);
  }  
}


실행을 하면 다음과 같이 정상값으로 나온다.
사용자 삽입 이미지
lock이란것으로 동시에 호출하는 쓰레드에 대해 작업중이니 기다리라는 명령을 내릴 수 있는것이다. 그러나 기능의 다양성을 원한다면 lock보다는 "Monitor.Enter()"와 "Monitor.Exit()"를 써주기 바란다.

  public void runit(){
   Monitor.Enter(this);
   int tp = a + 1;
   Console.WriteLine(tp.ToString());
   Thread.Sleep(10);
   a = tp;   
    Monitor.Exit(this);
  }

반응형
Posted by blueasa
, |