본문 바로가기

3.구현/기타언어

C++개발자 위한 C샵 배우기

1. C#를 간단히 배워보자

현재 저자의 허락을 받지 않고 내용을 요약 정리했습니다. 원본은 codeproject에 있고 아래 참조 부분을 참고하십시요. 저작권에 문제가 있다면 삭제하겠습니다.

(글쓴이 2007.10.22 박재성, ospace114@empal.com)

가. 소개

C#은 C++특징을 가지고 있으면서 자바 형식 프로그래밍 스타일을 갖고 있다. C++을 알고 있는 개발자는 쉽게 배울 수 있다. 그래서 이 글은 C++을 알고 있다는 전제하에 작성된다.

앞으로 다룰 내용

  • Program structure
  • Namespaces
  • Data types
  • Variables
  • Operators and expressions
  • Enumerations
  • Statements
  • Classes and structs
  • Modifiers
  • Properties
  • Interfaces
  • Function parameters
  • Arrays
  • Indexers
  • Boxing and unboxing
  • Delegates
  • Inheritance and polymorphism

다음 내용은 다른 곳에서 참조

  • Things which are common in C++ and C#.
  • Concepts like garbage collection, threading, file processing etc.
  • Data type conversions
  • Exception handling
  • .NET library

나. 프로그램 구조

C++과 같이 C#은 대소문자를 구분하며 세미콜론(;)으로 명령어를 구분한다. 다른 점은 선언(헤더파일)과 구현(CPP파일)을 따로 구분하지 않는다. 모든 코드는 확장자가 cs를 가진다.

모든 프로그램의 대표적인 예제인 Hello world!를 보자

using System; // 기본 네임 스페이스

namespace MyNameSpace
{
  class HelloWorld
  {

     static void Main(string[] args)
     {
        Console.writeLine("Hello world!");
     }
  }
}

1) 다른 객체에서 함수 호출시 네임 스페이스 사용

...

using AnotherNameSpace;

namespace MyNameSpace
{
  class HellowWorld
  {
     static void Main(string[] args)
     {
        AnotherClass obj = new AnotherClass();
        obj.Func(); // AnotherNameSpace에 있는 AnotherClass 사용
     }
  }
}

다. 변수 정의

변수명은 C++과 비슷하지만, 다른점이 있다.

  • C#에서는 변수는 액세스 전에 항상 초기화해야 한다. 그렇지 않으면 컴파일 에러 발생.
  • C#에서는 dangling 포인터를 액세스 할수 없다.
  • 전역변수는 없고 함수도 없다. 비슷한 기능으로 static를 사용해서 구현

라. 데이터형

기본 타임과 사용자 정의 타입이 있다.

1) 기본타입

byte, sbyte(signed), short, ushort, int, uint, long, ulong, float, double, sdecimal, string, char, bool

값의 범위는 C#과 C++간에 차이가 있다. C++에서는 long은 4bytes, C#에서는 8bytes이다.

2) 사용자 정의 타입

Class, struct Interface

마. 메모리 할당

메모리에 할당 형태로 데이터 타입에 따라 할당 방법이 달라진다.

1) Value type

스택에 할당되면 다음과 같은 데이터 타입이 있다.

  • 기본 혹은 내장된 데이터 타입(string 제외)
  • struct
  • Enum 타입

2) Reference type

힙에 할당되며, 더 이상 사용되지 않으면 가비지 컬렉션 해줘야 함.
new 연산자로 할당하지만 C++과 다르게 delete가 없다. 단, 명시적으로 해제할 경우 delete를 사용한다.

  • Class
  • Interface
  • Array같은 컬랙션 타입
  • string

2. Class와 Struct

C++과 비슷하나 메모리 할당 방법에 차이가 있다. Class은 new를 사용한 힙에 할당, struct는 스택으로 할당된다.
struct는 가볍고 빠르며, 무거운 데이터는 class를 사용해서 다룬다.

가. 구조

struct Date
{
  int day;
  int month;
  int year;
}


class Date
{
  int day;
  int month;
  int year;
  string weekday;
  string monthName;
  public int GetDay() { return day; }
  public int GetMonth() { return month; }
}

나. Properties

C++의 객체 지향에 익숙하다면 property(속성)을 잘 알고 있다. 예를 들어 앞 예제에서 속성은 day, month, year이다. 이 속성에 대해 C++에서는 Get, Set 메소드를 정의해서 사용하나 C#에서는 더욱 편한 방법을 제공한다.

using System;

class Date
{
  public int Day // 대문자로 시작
  {
     get { return day; }
     set { day = value; }
  }

  int day; // 속성은 소문자로 되어 있다.

  public int Month
  {
     get { return month; }
     set { month = value; }
  }

  int month;
  ...
  public bool IsLeafYear(int year)
  {
     return year%4 == 0 ? true; false;
  }
}

다음은 앞의 코드를 실제 사용한 예

class User
{
  public static void Main(string[] args)
  {
     Date date = new Date();
     date.Day = 27; // 속성 접근시 대문자로 된 메소드로 접근(주의!)
     date.Month = 7;
     ...
     Console.writeLine("Date: {0}/{1}/{2}", date.Day, date.Month, date.Year);
  }
}

다. Modifier

C++과 비슷하게 public, private, protected modifier를 사용하기에 C#에서 추가된 새로운 modifier만 다룬다.

1) readonly

  • 클래스 멤버에만 사용하며 이를 사용한 멤버는 단지 읽기만 가능하다.
  • 초기화 때나 생성시 값 할당때만 한번 기록 가능하다.
  • 상수인 const와 비슷하지만 const는 선언시 초기화해야하며 생성시 할당은 하지 못한다.
class MyClass
{
  const int constInt = 100; // 직접 초기화
  readonly int myInt = 5;  // 직접 초기화
  readonly int myInt2;
  public MyClass()
  {
     myInt2 = 8;   // 생성시 초기화
  }
  public void Func()
  {
     myInt2 = 7; // 에러
     ...
  }
}

3. Interface

COM을 알고 있다면 무엇인지 쉽게 알 수 있다. 추상 기반 클래스로 함수 시그니처만 가진 클래스를 인터페이스라 한다.
구현은 자식 클래스에서한다. C#에서는 클래스 다중 상속이 불가능하며, 다중 상속은 Interface를 통해서 가능하다.

using System;

interface myDrawing
{
  int orignx
  {
     get;
     set;
  }

  int origny
  {
     get;
     set;
  }

  void Draw(object shape);
}

class Shape : myDrawing
{
  int OrigX;
  int OrigY;

  public int orignx
  {
     get { return OrigX; }
     set { OrigX = value; }
  }

  public int origny
  {
     get { return OrigY; }
     set { OrigY = value; }
  }

  public void Draw(object shape)
  {
     ...
  }

  public void MoveShape(int newX, int newY)
  {
     ...
  }
}

4. Array

C#의 배열은 C++보다 뛰어나다. 힙에 할당되기에 reference type이다. 특히 배열 경계를 벗어나면 에러가 발생하며 엑세스가 불가능하다. 몇몇 함수는 배열에 대해 interate가 가능하다. foreach가 이란 iteration의 일종이다.

C++과 C#의 문법 차이

  • C#에서는 대괄호([ ])는 타입 뒤에 나오면 변수명 뒤에는 절대로 올 수 없다.
  • C#는 구성요소 할당시 반드시 new 연산자를 사용한다.

C#는 1차원 배열을 지원하며 다차원을 사용하기 위해서는 jagged array(배열의 배열)을 사용한다.

int[] array = new int[10];
for(int I = 0; i<array.Length; ++i)
  array[i] = i;
int[,] array2 = new int[5, 10];
array2[1,2] = 5;
int[][] arrayObj = new int[2];
arrayObj[0] = new int[4];
arrayObj[1] = new int[]{1, 2, 15};

5. Indexes

Indexes는 Colection에서 요소 엑세스할대 사용. 즉, 배열처럼 [ ]로 간단히 사용가능하다.
Indexer의 문법은 클래스 속성과 비슷하지만 입력인자(배열 인덱스역활)가 필요하다.

class Shape : CollectionBase
{
  public void add(Shape shape)
  {
     List.add(shape);
  }

  public Shape this[int index]
  {
     get{ return (Shape) List[index]; }
     set { List[index] = value; }
  }
}

6. Boxing/Unboxing

Boxing에서 C#에서 처음소개 됨.
모든 기본타입, 사용자 정의 타입은 Object라는 기본 클래스를 상속하며, 이는 System 네임 스페이스에 있다.
기본 또는 원시(Primitive) 타입을 Object로 패킹(Packing)하는 것을 boxing이라 한다. 그 반대가 unboxing이다.

class Test
{
  static void Main()
  {
     int myInt = 12;
     object obj = myInt; // boxing
     int myInt2 = (int) obj; // unboxing
  }
}

7. Function parameter

C#에서 파라미터는 다음 세 가지 타입이 가능하다.

  • By-Value / In parameters
  • By-Reference / In-Out parameters
  • Out parameters

가. By-value / In parameters

값 파라미터는 C++과 동일. 값을 복재해서 함수로 넘겨준다.

SetDay(5);
...
void SetDay(int day) { ... }

나. By-reference / In-Out parameters

C++의 포인터나 참조연산자(&)를 사용한 것과 같다.
C#에서는 더욱 에러가 적은 error prone이다.
In-Out parameter로 입력 값 넘겨주고 출력 값을 얻는다.
미초기화된 참조 파라미터를 넘겨줄 수 없다.
C$에서 ref 키워드 사용해서 참조 파라미터를 표현.
인자와 ref를 사용해서 함수로 넘겨줌으로서 참조 파라미터를 사용을 표시한다.

int a = 5;
FunctionA(ref a);
Console.WriteLine(9);

void FunctionA(ref int val)
{
  int x = val;
   val = x * 4;
}

다. Out parameter

이는 반환 파라미터에만 사용되며, 입력은 불필요하다.

out 키워드 사용.

int val;
GetNodeValue(val);
...
bool GeetNodeValue(out int val)
{
   val = value;
  return true;
}

라. Variable number of parameters and arrays

C#에서 배열을 함수 인자로 넘겨줄 때 params 키워드를 사용
함수 인자 내에서 배열 타입 파라미터는 가장 오른쪽에 위치
배열 한 개만 인자로 넘어가면 배열 형에 맞는 임의개수 배열 개수를 넘길 수 있다.

void Func(params int[] array)
{
  Console.WriteLine("Number of elements {0}", array.Length);
}
...

Func(); // 0출력
Func(5); // 1출력
Func(7, 9); // 2출력
Func(new int[]{3,8,10}); // 3출력
int array[] array = new int[8]{1, 3, 4, 5, 6, 7, 5};
Func(array); // 8출력

8. Operations and expressions

대부분 연산자는 C++과 동일하고 여기서는 새롭고 유용한 연산자 추가기능을 살펴보자.

가. is 연산자

is 연산자는 operand type이 같은지 변환 가능한지 확인할 때 사용된다. 폴리모피즘에 유용.
두 개 operand를 얻어서 boolean으로 결과 리턴.

void function(object param)
{
  if(param is classA)
     ...
  else if(param is MyStruct)
     ...
}

나. as 연산자

as 연산자은 operation 타입이 변환가능 하거나 같다면 해당 타입으로 변환되거나 boxed object로 결과를 반환(boxing)변환 안되거나 boxing 안되면 null로 리턴된다.

Shape shp = new Shape();
Vehicle veh = shp as Vehicle; // 결과는 null, 변환불가
Circle cir = new Cirecle();
Shape shp = cir;
Circle cir2 = shp as Circle;

Object[] objects = new object[2];
objects[1] = "Aisha";
objects[2] = new Shape();

String str;
for(int I = 0; i<objects.Length; ++i)
{
  str = objects[i] as String;
  if(str == null)
     Console.WriteLine("can not be converted");
  else
     Console.WriteLine("{0}", str);
}

9. Statements

statement도 C++과 비슷하며 몇몇 새로 추가된 statements와 modifiers만 다룬다.

가. foreach

배열 같은 collection의 iteration용

foreach (String s in array)
  Console.WriteLine(s);

나. lock

쓰레드 코드에서 critical section 영역을 locking(잠금)하는데 사용된다.

다. checked / unchecked

숫자 연산에서 오버플로우 확인용.

int x = Int32.MaxValue;

++x; // overflow
checked
{
  ++x; // exception
}

Unchecked
{
  ++x; // overflow
}

라. switch

  • case문에서 프로그램 흐름상 다음 case문으로 점프가 불가능하며 반드시 break를 사용해야 한다.
int var = 100;
switch(var)
{
  case 100: Console.WriteLine("100"); //C#에서 에러 발생
  case 200: Console.WriteLine("200"); break;
}

• C++과 비슷하게 사용하려면,

switch(var)
{
  case 100:
  case 200: Console.WriteLine("100, 200"); break;
}

• case 값으로 상수 값을 사용할 수 있다.

const String WeekEnd = "Sunday";
const String WeekDay1 = "Monday";
...
String WeekDay = Console.readLine();

switch(WeekDay)
{
  case WeekEnd: Console.WriteLine("It's weekend!"); break;
  case WeekDay1: Console.WriteLine("It's Monday"); break;
}

마. Delegates

이는 함수 참조를 변수에 저장할 수 있게 한다. C++에서는 typedef을 사용해 함수 포인터를 사용해서 저장한다.
C#에서 delegate을 이용해서 함수 선언해서 사용한다.

delegate int Operation(int val1, int val2);

public int Add(int val1, int val2)
{
  return val1 + val2;
}

public int Subtract(int val1, int val2)
{
  return val1 - val2;
}

public void Perform()
{
  Operation oper;
  Console.WriteLine("Enter + or - ");
  String optor = Console.ReadLine();
  Console.WriteLine("Enter 2 operands");
  String opnd1 = Console.ReadLine(0);
  String opnd2 = Console.ReadLine(0);
  int val1 = Convert.ToInt32(opnd1);
  int val2 = Convert.ToInt32(opnd2);

  if(optor == "+")
      oper = new Operation(Add);
  else
      oper = new Operation(Subtract);

  Console.WriteLine("Result = {0}", oper(val1, val2));
}

10. Inheritance and polymorphism

C#에서는 단일 상속만 가능하며 다중 상속은 interface를 사용해야한다.

class Parent { ... }
class Child : Parent;

가. virtual function

가상함수는 폴리모피즘(다형성) 개념을 구현. 단지, 자식 클래스에서는 override 키워드를 사용하여 함수 구현을 한다.

부모클래스는 virtual 키워드 사용.

class Shape
{
  public virtual void Draw()
  {
     Console.WriteLine("Shape.Draw");
  }
}

class Rectangle : Shape
{
  public override void Draw()
  {
     Console.WriteLine("Rectangle.Draw");
  }
}

class Square : Rectangle
{
  public override void Draw()
  {
     Console.WriteLine("Square.Draw");
  }
}

class MainClass
{
  static void Main(string[] args)
  {
     Shape[] shp = new Shape[3];
     Rectangle rect = new Rectange();
     shp[0] = new Shape();
     shp[1] = rect;
     shp[2] = new Square();
     shp[0].Draw(); // Shape.Draw 출력
     shp[1].Draw(); // Rectangle.Draw 출력
     shp[2].Draw(); // Square.Draw 출력
  }
}

나. Hiding parentfunctions using "new"

자식 클래스에 새로운 버전 함수 추가 가능하다.
new 키워드 사용해서 새로운 버전 함수 정의한다.
다음 예제는 앞의 예제를 수정했으며, override대신에 new 키워드를 사용했다.

class Shape
{
  public virtual void Draw()
  {
     Console.WriteLine("Shape.Draw");
  }

  class Rectangle : Shape
  {
        public new void Draw()
        {
           Console.WriteLine("Rectangle.Draw");
        }
  }

  class Square : Rectangle
  {
     public new void Draw()
     {
        Console.WriteLine("Square.Draw");
     }
  }

  class MainClass
  {
     static void Main(string[] args)
     {
        Console.WriteLine("Using Polymorphism: ");
        Shape[] shp = new Shape[3];
        Rectangle rect = new Rectange();
        shp[0] = new Shape();
        shp[1] = rect;
        shp[2] = new Square();
        shp[0].Draw(); // Shape.Draw 출력
        shp[1].Draw(); // Shape.Draw 출력
        shp[2].Draw(); // Shape.Draw 출력
        Console.WriteLine("Using without Polymorphism");
        rect.Draw(); // Rectangle.Draw 출력
        Square squ = new Square();
        squ.Draw(); // Square.Draw 출력
  }
}

보시다시피 다형성이 사용되지 않는다. 즉, 전혀 다른 Draw 메소드로 인식한다.
이는 naming에서 혼란을 피하기 위해서 사용된다.

주의: 같은 클래스에서 새로운 두 가지 버전을 동시에 사용불가. 즉 하나는 new로 다른 하나는 virtual 혹은 override로 선언할 수 없다.

다. Calling base class members

자식 클래스가 부모 클래스와 같은 이름을 가진 데이터 멤버가 있다면, 이런 이름 충돌을 피하기 위해 부모 클래스 데이터 멤버와 함수는 base 키워드 사용해서 액세스한다.

public Child(int val) : base (val)
{
  myVal = 5;
  base.myVal;
}

혹은

public Child(int val)
{
  base(val);
  myVal = 5;
  base.myVal;
}

11. 참고

Quick C# by Aisha Ikram. (http://www.codeproject.com/csharp/quickcsharp.asp)

반응형

'3.구현 > 기타언어' 카테고리의 다른 글

Rust 배우기3 - 활용  (2) 2024.01.10
Rust 배우기2 - 심화  (2) 2024.01.06
Rust 배우기1 - 기본  (2) 2024.01.04
[Flash] 3D Action Script 활용  (0) 2007.05.22