3. C# 기초

C++에서 기능이 더 추가되서 C# 이라는 이름을 사용함. 2000년 6월에 만들어진 언어다.

3.1. 네이밍 컨벤션

1. PascalCasing Convention - all public member, type, namespace

2. camelCasing Convention - parameter names

3.2. 자료형

제공하는 자료형은 다음과 같다.

  • Value type 값 형식
  • Reference type 참조 형식

int는 Int32 구조체(struct)이다. string은 System.String의 약어이다.

3.2.1. 값 형식

상수 필드나 지역 상수로 사용할 수 있다. (const는 심플 타입과 문자열 리터럴에 사용 가능):

const int x = 0; // only used for simple types
private readonly string ProductName = "C#"; //computed at compile-time

문자

  • char. 2byte unicode

숫자

  • short. 2bytes
  • int. 4bytes
  • long. 8bytes

실수

  • float. 4bytes
  • double. 8bytes
  • decimal. 16bytes

불리언

  • bool. 1byte

signed vs unsigned

  • 음수: 2의 보수법으로 생성

3.2.2. 형변환

형변환은 반드시 Type Cast Operator 나 메서드를 이용해서 명시적으로 형변환을 해야한다. 참조 타입의 경우 as 키워드를 사용해서 변환할 수 있다.:

long b = 3;
int a = (int)b;
string c = "hello";
a = int.Parse(c);
a = Convert.ToInt32(c);

3.2.3. 문자열

연결:

string s = "hello" + "world";

Escape sequence는 특수문자를 나타낼때 사용한다.

문자열에서 Escape sequence를 사용하고 않고 문자 그대로를 사용하고 싶다면 @ 을 문자, 문자열 리터럴 앞에 붙이면 된다.

3.2.4. 배열

System.Array의 메서드와 필드를 사용할 수 있다.

예시:

int[] ar = new Int[10];
int[] ar1 = {1,2,3};
int[,] ar2 = new int[2,2];
int[,] ar3 = new int[2,2]{{1,2},{3,4}};
int[,] ar4 = new int[,]{{1,2},{3,4}};

정렬:

Array.Sort(ar1); //Call static method

복사:

ar1.Clone(); //Call instance method

기타:

Array.Clear(ar,0,ar.Length);

3.2.5. 클래스

클래스는 데이터와 데이터의 연산을 함께 저장하는 데이터 구조이다. (속성, 이벤트, 연산자, 생성자도 갖는다.)

  • 정적 클래스: static 한정자(Modifier)를 갖으며 오직 정적 멤버만 갖고 인스턴스 생성이 안되는 클래스를 뜻한다.

3.2.5.1. 생성자

인스턴스 생성자는 클래스의 인스턴스의 초기화 코드를 정의한 멤버이다.

3.2.5.2. 메서드

클래스에서 계산이나 특정 동작을 수행하는 코드를 뜻한다. 메서드는 정적 메서드놔 인스턴스 메서드로 나눠진다.

매개변수를 전달하는 방법이 3가지 있다.

  • 값 매개변수(value parameter) : 기본값을 명시함으로써 선택적(optional) 매개변수를 사용할 수 있다.
  • 참조 매개변수(reference parameter) : 결과값이 여러개 일때 사용(입력,출력값을 모두 사용할때)
  • 출력 매개변수(output paramter) : 결과값이 여러개일때 사용(호출자가 전달한 매개변수의 값이 중요하지 않을때, 함수 종료 시 출력 매개변수를 할당해야 함, 다른 매개변수와 달리 초기값이 없어도 됨)

3.2.5.3. 확장 메서드(Extension method)

클래스에 없는 기능을 추가할때 사용할 수 있는 멤버이다.

첫번째 파라미터에 this 한정자를 갖고 포인터는 파라미터 타입으로 사용할 수 없다. 확장 메서드는 제네릭이나 중첩 클래스에서는 사용할 수 없다. 기존의 클래스에 없는 기능을 확장할때 좋다. (스칼라의 암시적 변환과 유사함)

3.2.5.4. 속성,프로퍼티(Properties)

객체나 클래스의 데이터에 접근할때 사용하는 멤버이다. 사용하는 방법이 필드와 비슷하다. 프로퍼티는 별도의 저장소를 갖고 있지 않으며 항상 접근자(accessors)를 통해서 값에 접근한다.

자동 구현 속성(Automatically implemented properties) 는 숨겨진 필드를 만들고 접근자로 필드를 접근하도록 자동 구현되는 속성이다.

아래 2개의 클래스는 같은 의미를 지닌다.:

public class Point {
  public int X { get; set; } //자동 구현
  public int Y { get; set; } //자동 구현
}

public class Point {
  public int x;
  public int y;
  public int X { get { return x; } set { x = value; } }
  public int Y { get { return y; } set { y = value; } }

}

자동 구현 속성 은 읽기전용, 쓰기전용이 허락되지 않는다.

3.2.5.5. 이벤트(Events)

객체나 클래스에서 알림을 주고 싶을때 사용하는 클래스의 멤버이다. 이벤트가 발생할 경우 등록된 이벤트 핸들러(Event Handler) 가 호출된다.

반드시 이벤트는 대리자(delegate) 타입이여야 한다.:

class ...{
  delegate void MyEventHandler (object sender, EventArgs e);//대리자 선언
  public event MyEventHandler MyClick;//대리자 타입의 이벤트 멤버 선언
}

3.2.5.6. 인덱서(Indexers)

배열과 같이 객체의 데이터를 인덱스를 통해서 접근하도록 해주는 멤버이다. 인덱서는 멤버의 이름이 [ ] 구분자로 묶인 파라미터 리스트 가 따르는 this 라는 점을 제외하고 속성 과 유사하다.

3.2.5.7. 소멸자(Destructors)

인스턴스가 제거될때 호출되는 코드를 정의한 멤버이다.

3.2.6. 대리자(delegate)

대리자는 1개 이상의 메서드의 참조값과 인스턴스를 함께 저장하는 데이터 타입 이다. 내부적으로 System.Delegate를 상속받은 클래스이다.

대리자는 내부적으로 호출 리스트(invocation list) 를 사용하여 1개 이상의 메서드를 저장한다. 각각의 메서드는 호출가능한 개체(callable entity)로 불린다. 인스턴스 메서드 를 위해 객체와 인스턴스로 구성된 호출가능한 개체를 저장하며 정적 메서드 를 위해서는 메서드로 구성된 호출가능한 개체를 저장한다.

예시:

delegate void MyDelegate(string s);
MyDelegate d;
d += (string s) => Console.Write("Hi " + s);
d += new MyDelegate(Hello) // 대리자의 생성자에 메서드를 넣어서 사용할 수 있음
d += Hello // 정적 메서드를 사용할 수 있음
d += delegate(){}
d("changwon");

3.2.7. 열거형(Enumeration)

이름있는 상수를 뜻한다. 열거형은 값 타입의 한 종류이다.

3.2.8. 구조체

클래스와 유사하게 필드와 함수를 갖는 데이터 타입이다. 클래스와 달리 힙 할당을 하지 않고 참조값이 아닌 직접 데이터의 구조를 저장 한다. 기본적으로 구조체는 internal, 필드는 private을 사용한다.

3.2.9. 인터페이스

인터페이스는 기능에 대한 계약(contract)이다. .NET Remoting, WCF에서 원격 메서드 호출을 위해 인터페이스가 사용된다. 구현을 공개하지 않고 인터페이스만 제공함으로써 보안성도 제공한다.

보통 대문자 I 로 시작한다.

인터페이스를 구현한 클래스에서 명시적 또는 암시적으로 멤버를 구현할 수 있다.

암시적 구현은 다음과 같다.:

interface IBeverage
{
  int GetTemp();
  void Paint();
}

class Coffee : IBeverage
{
  public int GetTemp(){ return 0;}
  public void Paint()
  {

  }
}

명시적 구현은 다음과 같다.:

interface IBeverage
{
  int GetTemp();
  void Paint();
}

class Coffee : IBeverage
{
  int IBeverage.GetTemp(){ return 0;}
  void IBeverage.Paint()
  {

  }
}

인터페이스 멤버의 명시적 구현은 크게 2가지 목적으로 사용된다.

  • 인터페이스 멤버의 명시적 구현이 클래스 구현으로 부터 제외되기 때문에, 클래스 사용자 입장에서 인터페이스 메서드가 필요 없을 경우에 유용하다.
  • 다중 상속을 하였을때 여러개의 동일한 메서드 시그니처를 사용할 수 있다.

명시적 구현은 인터페이스 인스턴스를 통해서만 호출이 가능하다. 위의 예제에서 Coffee 클래스를 IBeverage 타입으로 형변환 한 뒤 구현된 메서드를 호출할 수 있다. 또한 접근 한정자 를 사용할 수 없다. 그리고 abstract, virtual, override, static 도 사용할 수 없다.

또한 명시적 구현은 메서드의 전체 이름으로 호출할 수 없다. 따라서 private멤버로 보이지만 인터페이스 인스턴스를 사용하면 public 멤버처럼 접근할 수 있다.

3.2.10. 접근자(Accessors)

접근자에는 프로퍼티를 읽고 쓸때 실행되는 코드가 정의되어 있다.

3.2.11. 메서드 오버로드

유일한 메서드 시그니처를 바탕으로 같은 메서드 이름을 사용할 수 있는 특징을 뜻한다.

3.2.12. 명명된 인자

다음과 같이 인자에 이름을 붙여서 사용할 수 있다.:

StopService(true, serviceID: 1)

3.2.13. 접근 한정자(Accessibility)

  • public. 접근에 제한이 없음
  • protected. 파생된 타입이나 현재 클래스에서 접근 가능
  • protected internal. 파생된 타입이나 현재 어셈블리에서 접근 가능
  • internal. 현재 어셈블리에서 접근 가능
  • private. 현재 클래스에서만 접근 가능

클래스, 구조체와 같은 타입은 public 이나 internal 접근 한정자를 사용할 수 있다. 클래스 멤버는 5가지 종류의 접근 한정자를 갖을 수 있다. 네임 스페이스는 한정자를 사용할 수 없다.

기본 접근 한정자는 다음과 같다.

  • 타입은 기본적으로 internal 접근 한정자를 갖는다. (클래스의 멤버로 선언된 타입은 제외)
  • 클래스 멤버는 기본적으로 private 접근자를 갖는다.

3.3. 예외처리

예시:

try{
  string a = "12345x";
  int x = Convert.ToInt32(a); //예외 발생
}catch(Exception e){
  MessageBox.Show("Error: " + e.Message);
}

3.4. 타입 파라미터

제네릭 타입(generic type), 언바운드 제네릭 타입(unbound generic type), 제네릭 클래스, 제네릭 인터페이스 은 타입 인자를 전달하여 다른 타입을 생성할때 사용된다. 다른 언어에서는 제네릭 클래스/트레이트 처럼 해석되기도 한다.

타입 인자가 전달된 경우 생성된 타입(constructed type) 이라 불린다. 또는 파라미터화된 타입 라고 불린다.

타입 파라미터는 클래스의 멤버의 타입을 정의하기 위해 클래스에 명시할 수 있다.

3.5. 상속(Inheritance)

이미 만들어 놓은 클래스의 멤버를 재사용할때 사용한다. 베이스 클래스로부터 접근 한정자에 상관없이 생성자, 소멸자, 정적 생성자를 제외하고 모든 멤버를 상속 받는다. 또한 상속은 전이되기 때문에 베이스 클래스의 베이스 클래스의 멤버도 상속받는다.

용어

  • Super class <-> Sub class (자바)
  • Base class <-> Derived class (C++, C#)
  • Parent class <-> Child class

특징

  • 파생 클래스를 베이스 클래스 타입으로 바꿀 경우 암시적 변환이 일어난다. 즉 파생클래서의 인스턴스는 베이스 클래스의 타입처럼 다룰 수 있다.
  • virtual 클래스를 사용하지 않을 경우 같은 이름과 같은 시그니처를 갖는 메서드를 만들어서 베이스 클래스의 멤버를 숨길 수(hide) 있다.
  • virtual 키워드를 통해 메서드, 프로퍼티, 인덱서를 재정의 할 수 있다. 이는 다형성(polymorphic) 동작을 할 수 있게 해준다. 즉 메서드를 호출하는 시점의 인스턴스 타입에 따라 실제 호출되는 메서드를 다르게 할 수 있다는 뜻이다.

재정의(override) 관련 키워드

  • override : 베이스 클래스의 메서드를 오버라이드 할때 사용할 수 있다.
  • virtual : 파생 클래스에서 재정의할 경우 메서드를 오버라이드 가능하게 한다. 이 키워드는 다형성 동작을 할 수 있게 한다.

3.5.1. 추상 클래스와 메서드

abstract 라는 한정자를 쓸 경우 추상 메서드(abstract method) 라고 불린다. 추상 메서드는 virtual 한정자가 암시적으로 들어가므로 virtual 키워드를 사용할 수 없다.

추상 메서드는 추상 클래스(abstract class)에서만 선언할 수 있다. 추상 클래스의 추상 클래스가 아닌 파생 클래스는 반드시 추상 메서드를 오버라이드 해야한다.

3.5.2. 봉인 클래스(sealed class)

sealed 한정자는 파생 클래스를 만들지 못하게 막는다. sealed는 추상 클래스에 사용할 수 없다.

목적 - 의도치 않은 상속을 막을 수 있으며 런타임 최적화를 가능하게 한다.

3.6. 구문(statement)

3.6.1. foreach

foreach는 콜렉션(Collection)의 원소를 열거하고 각 원소를 열거할때마다 내장된 구문을 실행하는 구문이다.

3.7. Collection

자주 사용하는 제네릭이다.

  • List<T>
  • Dictionary<K,V>
  • Stack<T>
  • Queue<T>

3.8. 익명 함수 표현(anonymous function)

람다 표현식 (타입이 없어도 가능, 블록문 없이 명령구문을 바로 사용 가능, 괄호 생략 불가):

(string s) => { Console.WriteLine(s); }
(string s) => Console.WriteLine(s);
s => expr

익명 메서드 표현식 (파라미터 리스트에 타입 필요, 블록문이 필요, 괄호 생략 가능):

delegate (string s) { Console.WriteLine(s); }

3.8.1. 외부변수(Outer variables)

외부변수란 람다 표현식 또는 익멩 메서드 표현식이 포함되어 있는 스코프의 로컬변수, 값 파라미터, 파라미터 배열이다. 익명 함수에서 참조된다면 캡처(captured) 되었다고 한다.

로컬 변수나 값 파라미터가 캡처(captured)된다면 이는 더이상 고정된 값이 아닌 변경가능한 변수로 바뀐다. 외부변수는 사용된 익명함수가 가비지 컬렉션될때까지 존재한다.

3.9. LINQ(Language Integrated Query)

LINQ는 에릭 마이어가 만든 C#의 통합 질의 기술들의 집합이다.

종류

  • LINQ to Object.
  • LINQ to DataSet.
  • LINQ to XML.
  • LINQ to Entity.
  • LINQ to SQL.

3.9.1. 쿼리 표현(Query Expressions)

예시:

var num = 1
var a = from a in db.Employees
        where a.id = num
        select a

3.9.2. 익명 타입(Anonymous type)

컴파일러에 의해서 자동 생성되며 프로그럄에서 참조될 수 없는 타입을 뜻한다. 프로퍼티의 이름과 순서가 같을 경우 같은 타입에 대한 인스턴스가 생성된다.

예시:

var p1 = new { Name = "Changwon", Country = "Korea" };

3.9.3. 멀티 태스킹(Multi tasking)

Task 예시:

Task task = new Task(new Action(GetCurrentTime));
Task task2 = Task.Factory.StartNew(new Action(GetCurrentTime));
Task task3 = Task.Run(new Action(GetCurrentTime));
Task task4 = Task.Run(GetCurrentTime);

반환값 있는 Task:

Task<String> task = Task<String>.Run(GetCurrentTime)

UI 업데이트:

label1.Dispatcher.BeginInvoke(new Action(() => update(1)));

BackgroundWorker(UI 업데이트 가능):

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += bw_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;

lock은 주어진 객체를 사용해 쓰레드간 상호배재를 하도록 한다. lock구문의 파라미터로 반드시 레퍼런스 타입이 와야한다.

lock 구문 예시:

lock(object){
  ...
}

bool __lockWasTaken = false;
try{
  System.Threading.Monitor.Enter(x, ref __lockWasTaken);
  ...
}finally{
  if(__lockWasTaken) System.Threading.Monitor.Exit(x);
}