C#, 닷넷에서 readonly 키워드와 const 키워드는 비슷하지만 다른 키워드 이다.

두 키워드의 차이점 및 공통점에 대해서 알아보자. 


 const 키워드는 대부분에 프로그래밍 언어에서 지원하는 기본적인 문법입니다. 변수를 선언할때 변수 타입앞이나 뒤에 명시하여 해당 변수를 상수화 합니다. 그래서 선언할때 변수값을 지정하면 이 변수는 이제 상수가 되어 값을 읽을 수는 있지만 수정이 불가능 합니다. 이런 불편한 상수를 왜 사용할까 하지만 많은 장점이 존재 합니다. 


 예를들어 원에 넓이를 구하는 문제에서 파이값을 3.14로 해서 파이값 X 반지름 X 반지름 해서 넓이를 구하는 문제에서 파이값은 항상 같기 때문에 변수에 넣어두고 사용하면 됩니다. 하지만 실수로 파이값에 다른 숫자나 변수를 대입하여 값이 변경되면 에러가 발생하게 됩니다. 그래서 파이변수를 선언할 때 const 키워드를 넣어서 상수화 합니다. 그러면 절대 변경될 일이 없기때문에 안정적인 프로그램이 됩니다. 혹시라도 값을 대입하는 코드를 작성하면 컴파일 단계에서 에러가 발생하기 때문에 쉽게 버그를 수정할 수 있습니다. 

 그리고 파라미터로 객체의 주소값을 받으면 해당 객체를 수정하지 못하게 파라미터 선언에 const 키워드를 붙이면 해당 함수에서 파라미터로 넘어온 값을 수정할 수 없게 됩니다. const 키워드를 사용해도 되는 곳이라면 최대한 많이 사용하는 것이 안정적인 코드를 작성하는 방법 입니다. 



1. 공통점


두 키워드 모두 한번 값이 정해지면 수정을 할 수 없다.



아래 두 예제를 보자.

두 예제 모두 클래스에 string 변수를 선언해서 const 키워드를 사용하였다. 해당 test 변수는 상수가 되어서 선언한 곳 이외에서는 수정이 될 수 없다.

혹시라도 수정하려고하면 컴파일 에러가 발생하게 된다.


- const 예제

class TEST
{
	private const string test = "test1234";
	public string getTest()
	{
		return test;
	}
}

class Program
{
	static void Main(string[] args)	
	{
		TEST t1 = new TEST();
		Console.WriteLine("t1 : " + t1.getTest());
	}
}


- readonly 예제

class TEST2
{
	private const string test = "test1234";
	public string getTest()
	{
		return test;
	}
}

class Program
{
	static void Main(string[] args)	
	{
		TEST2 t2 = new TEST();
		Console.WriteLine("t2 : " + t2.getTest());
	}
}



2. 차이점


const키워드와 readonly 키워드의 가장 큰 차이점은 const와는 다르게 readonly 는 생성자에서 값을 초기화 할 수 있다. 아래 예제를 보자. 생성자를 통해 readonly 값을 수정하는 것을 볼 수 있습니다.


- readonly 예제

class TEST
{
	public readonly string test = "start";
	public TEST()
	{
		test = "abc";
	}
	public TEST(string s1)
	{
		test = s1;
	}
}
class Program
{
	static void Main(string[] args)
	{
		TEST t1 = new TEST();
		TEST t2 = new TEST("def");
		Console.WriteLine("t1 : " + t1.test);
		Console.WriteLine("t2 : " + t2.test);
	}
}


test는 readonly 선언을 하였고 초기화를 하였지만 생성자인 TEST()와 TEST(string s1)에서 다시 초기화를 하고있다.

const 선언을 하면 에러가 발생하게 된다.


그리고 const는 선언과 동시에 static이 컴파일러에 의해 자동으로 붙게된다. 정적 필드가 되는것이다. 하지만 readonly의 경우 그렇지 않기 때문에 선언적으로 static 키워드를 붙여주면 정적 필드로 사용이 가능하다.


확장성이 좋고 생성자에 따른 값도 변경할 수 있다. 되도록 const 키워드 이외에 readonly 키워드를 사용하는 것이 좋다.


주의할점은 아래코드와 같이 함수에 지역변수에는 readonly 키워드를 사용할 수 없다.







Posted by 꿈만은공돌
,

C와 C++에서 직접 new 연산자와 malloc 함수를 이용해서 힙(heap)영역에 동적 메모리 할당을 받고 delete 연산자와 free 함수를 이용해서 동적 메모리를 해제하게 된다.


C, C++에서 메모리 할당과 해제 원리 : http://hijuworld.tistory.com/28


하지만 C#과 JAVA 에서는 가비지 컬렉터(garbage collecter)에 의해서 메모리 할당과 메모리 해제가 이루어진다.


사용하지 않는 메모리를 가비지 컬렉션(garbage collection)이 해제 시킨다.


우선 간단하게 메모리를 할당하는 방식에 대해서 알아 보자.




1. C#에서 객체 생성시 메모리 할당 방식

new 키워드를 통해 객체를 생성하게 되면 힙(Heap) 메모리 영역에 객체가 할당된다. C언어와 C++과 다른점은 메모리 첫영역부터 차례대로 할당하게 되고 할당이 가능한 부분의 첫 주소에 포인터로 가르키게 된다. 다음에 또 객체를 생성하면 할당 가능한 메모리의 포인터 영역부터 메모리를 할당하고 다시 해당 포인터는 할당된 사이즈 만큼 증가하게 됩니다.


C, C++과 다르게 이렇게 하면 메모리 할당이 가능한 영역을 찾는 과정이 없기 때문에 속도가 훨씬 빠르다.

아래 그림을 보면 쉽게 이해가 갈것이다.








2. C#에서 Garbage Collector에 의한 메모리 해제 방식

Test a = new TEST();

a = null;

위와 같은 코드를 만나면 처음에 할당한 변수 a가 가지고 있는 객체는 더이상 사용하지 않기 때문에 메모리 해제의 대상이 됩니다.

힙영역에 메모리가 계속 쌓이다 보면 특정 시점에서 Garbage collector에 의해 사용되지 않는 메모리(아래 그림에서 B, D)는 해제하게 된다.

Root Reference 메모리 영역에서 사용하는 메모리 영역을 관리하게 되고(아래 그림의 A와 C) 객체에서 할당한 객체도 존재(아래 그림에서 E)한다. Root Reference를 탐색하여 A와 C를 찾고 다시 A와 C를 검색하여 E를 찾아낸다.

그러면 그외에 메모리 영역인 B와 D는 사용하지 않는 메모리 영역으로보고 메모리를 해제한다.

그리고 남은 A, C, E를 다시 재배치 한다.

그리고 다음 객체를 할당할 메모리의 포인터의 위치도 재배치 한다.




여기까지가 간단하게 설명한 메모리 할당 및 해제 원리이다.

사실 가비지 컬렉션에 방식은 이외에도 상당히 복잡하다.


1. 세대별 가비지 컬렉션 방식 : http://hijuworld.tistory.com/41


2. 용량이 큰 객체에 메모리 할당 해제 방식 : http://hijuworld.tistory.com/46


3. 가비지 컬렉션의 모드에 따른 방식


따라서 이 3가지 방식에 대해서 다음 포스팅에서 설명하겠다.




참고 사이트 및 서적 

- c# GC.Collect() 정리 : http://helloit.tistory.com/40

- 유경상의 닷넷 블로그 : http://www.simpleisbest.net/post/2011/04/01/Review-NET-Garbage-Collection.aspx

- MSDN : https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals

- 뇌를 자극하는 C# 4.0(한빛미디어, 박상현 지음)





Posted by 꿈만은공돌
,


C#에 컬렉션 중 하나인 Dictionary 이다.

아래 예제는 인자 추가(Add()) 및 삭제(RemoveAt()) 관한 내용이다.

using System.Collections.Generic;

var dict = new Dictionary<int, String>();

dict.Add(1, "one");
dict.Add(2, "two");
dict.Add(3, "three");
Console.WriteLine("----------1----------");
foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine(item.Key + " " + item.Value);
}

Console.WriteLine("----------2----------");
dict.Remove(2); //key값이 2인 두번째 인자 삭제
foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine(item.Key + " " + item.Value);
}


Posted by 꿈만은공돌
,

C#에 컬렉션 중 하나인 리스트(List) 이다.


아래 예제는 인자 추가(Add()) 및 삭제(RemoveAt()), 삽입(insert())에 관한 내용이다.


using System.Collections.Generic

var list = new List<string>(); // 선언
 
//Add() : 추가
list.Add("Test1");
list.Add("Test2");
list.Add("Test3");
Console.WriteLine("----------1----------");
foreach (string str in list)
{
    Console.WriteLine(str);
}
 
//RemoveAt() : 제거
list.RemoveAt(1); //두번째 인자인 TEST2 제거
Console.WriteLine("----------2----------");
foreach (string str in list)
{
    Console.WriteLine(str);
}
 
//Insert() : 삽입
list.Insert(1, "TEST4"); // 두번째 인자에 TEST4 삽입
Console.WriteLine("----------3----------");
foreach (string str in list)
{
    Console.WriteLine(str);
} 







Posted by 꿈만은공돌
,

정규표현식이라는 표현식을 이용하여 패턴이 일치 하는지 검사 할 수 있다. 예를들어 어떤 문자열이 A부터 C사이에 글자로 시작하면서 5글자이내의 글자인지를 검사하거나 해당 문자열이 숫자로만 이루어지고 10글자 이내의 글자인자를 검사할 수 있다. 정규표현식을 잘 작성하면 해당 문자열이 전화번호형식인지 이메일 형식인지 등을 쉽게 검사할 수 있다. 구글링을 통해 해당 정규표현식은 쉽게 찾을 수 있다.

 정규 표현식을 사용하여 패턴 매칭을 하는 것이 속도가 빠르고 정확하다. 사용자가 코드를 잘작성 하더라도 정규표현식을 이용하여 패턴 매칭을 하는 것보다 빠르기는 힘들다.




정규 표현식을 정리한 위키백과이다. 자세한내용은 아래 링크를 참고하도록 하자.

https://ko.wikipedia.org/wiki/%EC%A0%95%EA%B7%9C_%ED%91%9C%ED%98%84%EC%8B%9D


간단히 정리하자면 아래와 같다. 아래의 표현들만 알아도 어느정도 사용하는데 문제가 없다. 더 필요한 것이 있다면 위키를 참고하자.


. : 하나의 문자를 의미한다.
예제 : A.B 패턴은 AAB(일치), A1B(일치), AB(불일치), AAB(불일치) 이다.

? : 아무문자도 없거나 하나의 문자가 있는 것을 의미한다.

예제 : A?B 패턴은 AAB(일치), AB(일치), AAA(불일치), AAAB(불일치) 이다.


* : 0개나 하나이상의 문자가 있는 것을 의미한다.

예제 : A*B 패턴은 AB(일치), AAAAAAAAAB(일치), A123B(일치), AAAAAAAA(불일치) 이다.


+ : +앞에있는 문자가 한번이상 반복됨을 의미한다.

예제 : AB+ 패턴은 AB(일치), ABBBB(일치), ABC(불일치), A(불일치) 이다.


[] : [ ] 사이에 있는 형식이 일치하는것을 의미한다. 즉 [AB] 는 A, B만일치, [A-Z] 는 A부터 Z중 하나의 문자와 일치한다.

예제 : [A-C] 패턴은 A(일치), B(일치), AB(불일치), BC(불일치) 이다.


[^] : ^다음에 문자를 쓰면 해당 문자를 제외한다는 것을 의미한다.

예제 : [^A-C]D 패턴은 DD(일치), AD(불일치), DDD(불일치), D(불일치) 이다.


() : ( ) 사이에 문자가 하나의 묶음이된다. 즉 (ab)+ 는 abababab 와 일치한다.

예제 : (ab) 패턴은 ab(일치), ba(불일치) 이다.


{} : { } 사이에 숫자를 쓰면 그 숫자 만큼 패턴이 반복됨을 의미한다.

[A-C]{1,4}는 A,B,C 를 1개에서 4개 조합하면 된다. AAAA도 가능하고 A, BA, ABC, ACBA도 가능하다.

예제 : [A-C]{1,3} 패턴은 AA(일치), CBC(일치), ADA(불일치), ACCC(불일치)이다.


^ : 문자열의 시작을 의미한다.


@ : 문자열의 끝을 의미한다.




C#에서도 정규식을 사용하여 패턴 매칭을 할 수 있다. C#에서 제공해주는 정규식 관련 라이브러리는 사용하기 간단하다. 따로 라이브러리를 추가할 필요가 없기때문에 적극 활용하도록 하자.


정규 표현식 앞에 @를 쓰는거는 이스케이프(escape) 문자 기능을 동작하지 않게한다. 꼭 써주는 것이 좋다.


정규표현식을 위해선 Regex 클래스를 사용한다. 해당 클래스에 정규식표현을 가지고 객체를 만들면 된다. 그리고 Regex의 IsMatch 함수를 써서 패턴매칭을 한다. IsMatch 함수는 해당 패턴과 매칭하여 결과를 Bool 형식으로 반환한다. 일치하면 true, 일치하지 않으면 false를 반환한다.


아래는 사용 예제이다. 첫번째는 휴대폰 번호를 검사하는 것이고 두번째는 한글 이름 3글자를 검사하는 패턴 매칭이다. 

휴대전화 예제는 01 로 시작을 하고 그다음에 0,1,6,7,8 이 세번째 자리에 오고 그다음에 - 문자오고 0부터 9까지의 숫자로 4개의 조합을 만들고 다시 -가 오고 그다음에 0부터 9까지의 숫자로 4글자를 만든다.

3글자 이름을 검사하는 것은 가부터 힣까지의 글자(한글 전체)를 가지고 3가지 조합을 만들면 된다.


추가로 소문자는 a-z 라고하면 되고 대문자는 A-Z로 표현하면 된다.



네임스페이스 using System.Text.RegularExpressions; 을 추가해주어야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
String phoneNumber = "010-1234-5555"//"010-a234-b123"는 miss match
Regex regex = new Regex(@"^01[01678]-[0-9]{4}-[0-9]{4}$");
if (regex.IsMatch(phoneNumber))    
{
    Console.WriteLine("Match");
else 
{
    Console.WriteLine("Miss Match");
}
 
String name = "김공돌";        //"김공1"는 miss match
regex = new Regex(@"^[가-힣]{3}$");    //한글 3글자
if (regex.IsMatch(name))
{
    Console.WriteLine("Match");
}
else
{
    Console.WriteLine("Miss Match");
}





JAVA에서 정규식을 사용하여 패턴 매칭에 관한 포스팅이다. 참고하도록 하자. http://hijuworld.tistory.com/58
Posted by 꿈만은공돌
,

C언어에서 가장 불편한 것을 꼽으라면 문자열 처리가 그중 하나가 된다.


C#에서는 문자열을 쉽고 편리하게 사용할 수 있게 String 이라는 편리한 class를 제공해준다.


그리고 class는 다양한 함수들을 가지고 있는데 잘 사용한다면 문자열들을 다루는데 많은 도움이 된다.


1. Length : 문자열의 길이를 반환 한다.

아래 예제는 abcd def 라는 문자열에 길이인 8을 size 변수에 넣는 것을 알 수 있다.


1
2
3
4
5
string test = "abcd efg";
 
//Length : 글자 길이
int size = test.Length; //8
Console.WriteLine("size : " + size);
cs


출력결과

size : 8




2. Split : 특정 조건으로 문자를 쪼갤 수 있다.

아래 예제는 abcd efg 라는 문자열을 Split(' ') 으로 띄어쓰기 된 부분을 쪼개서 abcd와 efg로 나누는 것을 볼 수 있다.  


1
2
3
4
5
6
string test = "abcd efg";
 
string[] arrTest = test.Split(' '); 
foreach(string str in arrTest) {
    Console.WriteLine(str);
});
cs


출력결과 

abcd

def




3. IndexOf : 특정 문자의 위치를 찾는다. 첫번재 글자는 0을 기준으로 한다.

아래 예제는 abcd efg 문자열에서 Indexof(' ') 해서 공백을 찾아 해당 위치를 반환한다.


1
2
3
4
string test = "abcd efg";
 
int indexSpace = test.IndexOf(' '); //4
Console.WriteLine("index : " + indexSpace);
cs


출력결과

index : 4





4. [index] : 특정 index에 문자를 얻을 수 있다. 0번부터 시작한다.

아래 예제는 abcd efg 에서 index가 1인 b를 반환한다.


1
2
3
4
string test = "abcd efg";
 
char cTest = test[1]; //b
Console.WriteLine("index 1 char : " + cTest);
cs


출력결과

index 1 char : b




5. Substring(시작index, 글자수) : 문자열을 시작 index부터 글자수 만큼 쪼갤 수 있다. 글자수가 없다면 끝까지 쪼갠다.

아래예제는 abcdefghi 라는 문자열을 0번째 index부터 4글자인 abcd를 쪼개서 str1에 넣고 4번째 index부터 4글자인 efgh를 쪼개서 str2에 넣고 7번째 index부터 마지막까지인 hi를 str3에 넣었다.


1
2
3
4
5
6
7
8
9
string test = "abcdefghi";
 
string str1 = test.Substring(04);  //abcd
string str2 = test.Substring(44); //efgh
string str3 = test.Substring(7); //hi
 
Console.WriteLine("str1 : " + str1);
Console.WriteLine("str2 : " + str2);
Console.WriteLine("str3 : " + str3);
cs


출력결과

str1 : abcd

str2 : efgh

str3 : hi




6. StartsWith : 특정 문자로 시작하는지 검사

아래예제는 abcd efg 라는 문자열을 ab로 시작하는지 검사하고 cd로 시작하는지 검사하여 결과를 출력한다.


1
2
3
4
5
6
7
string test = "abcd efg";
 
bool startWith_ab = test.StartsWith("ab"); //True
Console.WriteLine("startWith ab : " + startWith_ab);
 
bool startWith_cd = test.StartsWith("cd"); //False
Console.WriteLine("startWith cd : " + startWith_cd);
cs


출력결과

startWith ab : True

startWith cd : False




7. Contains : 특정 문자열이 포함 되어있는지 검사

아래 예제는 abcd efg 문자열에서 cd가 포함되어있는지와 dc가 포함되어있는지 검사하여 출력한다.


1
2
3
4
5
6
7
string test = "abcd efg";
 
bool contain_cd = test.Contains("cd"); //True
Console.WriteLine("contain cd : " + contain_cd);
 
bool contain_dc = test.Contains("dc"); //False
Console.WriteLine("contain dc : " + contain_dc);
cs


출력결과

contain cd : True

contain dc : False




8. Trim : 앞과 뒤에 빈문자를 제거한다.

아래 예제는 " abcde wef   "의 문자열에 앞뒤에 붙은 문자열을 제거하여 출력한다.


1
2
3
4
string test = " abcde wef   ";
 
string trimStr = test.Trim(); //abc
Console.WriteLine("시작"+trimStr +"끝");
cs


출력결과

시작abcde wef끝

Posted by 꿈만은공돌
,


특정 구간에 코드 실행시간을 알아야내야 하는 경우에 필요한 포스팅 입니다.


코드를 작성하다 보면 딜레이가 발생하거나해서 어느 부분에 오버해드가 발생하여 시스템을 느리게하는지를 알아내야 하는 경우가 생길 수 있습니다.


또는 알고리즘을 공부하다보면 내가 구현한 알고리즘의 속도를 알고 싶을 때가 있습니다.


코드를 작성할때 중간중간 해당 함수나 코드가 느려질 것이 염려된다면 중간 중간 속도를 체크하면서 작성하면 좋습니다.



C# 에서 Stopwatch 클래스를 사용하여 프로그램의 수행시간을 측정할 수 있다.


네임스페이스로 using System.Diagnostics; 를 선언하고 Stopwatch 클래스를 사용하면 되다.



Stopwatch 클래스에 start 함수를 호출하면 시간측정을 시작하게 된다. 그리고 Stopwatch 클래스에 stop 함수를 호출 하면 시간측정이 끝나게 된다. 그리고 ElapsedMilliseconds 변수에 측정한 시간이 저장되게 된다. 그래서 해당 변수에 값을 출력하면 된다. 시간은 ms단위로 저장이 되기 때문에 만약 초단위로 보고 싶다면 /1000을 하면 된다.


아래는 사용 예제이다. 시간측정을하는 start와 stop을 하는 중간에 이중 for문을 사용하여 의미없는 연산을 계속하여 시간이 걸리게 하였다. 이중 for문에 i와 j의 조건값을 바꿔가며 측정시간이 어떻게 변하는지 직접 테스트해노는 것도 좋은 방법이다.

using System.Diagnostics;
 
class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopwatch = new Stopwatch(); //객체 선언
        stopwatch.Start(); // 시간측정 시작
 
        int sum = 0;
        for (int i = 0; i < 10000; i++)
        {
            for (int j = 0; j < 100000; j++)
            {
                sum = i * j;
            }
        }
 
        stopwatch.Stop(); //시간측정 끝
 
        System.Console.WriteLine("time : " + 
                           stopwatch.ElapsedMilliseconds + "ms");
    }
}



사용할 때 아래의 코드를 복사해서 사용하면 된다.


        Stopwatch stopwatch = new Stopwatch(); //객체 선언
        stopwatch.Start(); // 시간측정 시작
 
       
 
        stopwatch.Stop(); //시간측정 끝
 
        System.Console.WriteLine("time : " + stopwatch.ElapsedMilliseconds + "ms");
 



추가로 아래 자바와 C,C++의 시간측정 방법이다.


자바(java) 코드의 실행 시간 구하기(시간 측정) : http://hijuworld.tistory.com/2


C, C++ 프로그램 실행 시간 측정 : http://hijuworld.tistory.com/1




Posted by 꿈만은공돌
,

https://www.nuget.org/packages



Posted by 꿈만은공돌
,

C#에서는 데이터 타입을 쉽게 변환 할 수 있도록 하는 문법을 제공해 줍니다.


알아두면 편리합니다.


C#에서 String 타입에 문자열을 int형으로 변환하는 방법입니다.

int.Parse를 이용하여 아래와 같이 사용하면 됩니다.


1
2
3
String str = "12345";
int number = int.Parse(str); //변환
Console.WriteLine(number);  //출력




하지만 아래와 같이 String 타입의 문자열에 숫자가 아닌 문자가 들어있을 경우엔 에러가 발생하게 됩니다.


1
2
3
String str = "1a2";
int number = int.Parse(str);    //에러발생
Console.WriteLine(number);
cs


Unhandled Exception: System.FormatException: Input string was not in a correct format. 

이와같은 에러가 발생하게 됩니다.


이와 같은 에러발생을 방지하려면 TryParse() 함수를 이용하면 됩니다.

정상적으로 변환이 되면 true가 반환되고 실패하면 false가 반환이 됩니다.

함수의 원형은 아래와 같습니다.

bool int.TryParse(String s, out int number);




아래는 사용 예제입니다.


1
2
3
4
5
6
7
8
9
10
String str = "1a2";
int number;
if(int.TryParse(str, out number))
{
    Console.WriteLine(number);
}
else
{
    Console.WriteLine("fail");
}




int형 말고도 소수를 저장할 수 있는 double 형 역시 같은 방식으로 사용하면 됩니다.


1
2
3
4
5
6
7
8
9
10
String str = "123.4";
double number;
if(double.TryParse(str, out number))
{
    Console.WriteLine(number);
}
else
{
    Console.WriteLine("fail");
}
cs


날짜와 시간을 저장하는 자료형인 DateTime형 역시 아래와 같이 사용하면 됩니다.


1
2
3
4
5
6
7
8
9
10
String str = "17 april 2017";
DateTime dateTime;
if(DateTime.TryParse(str, out dateTime))
{
    Console.WriteLine(dateTime);
}
else
{
    Console.WriteLine("fail");
}

cs



Posted by 꿈만은공돌
,

C#에 언어가 업데이트 자주 되는 편이라 새로운 문법들을 학습해야 하는데 이런 새로운 내용들을 잘 정리해놓은 사이트이다.


최신 C#에 문법들을 설명한 사이트 주소


링크 : http://www.csharpstudy.com/Latest/CS7-new-features.aspx



Posted by 꿈만은공돌
,