'C#'에 해당되는 글 7건

  1. 2017.04.03 [C#] interface
  2. 2017.03.25 [C#] 메소드 오버로딩, 가변길이 매개 변수, 선택적 매개 변수
  3. 2017.03.24 [C#] 참조에 의한 매개 변수 전달. ref / 출력 전용 매개 변수. out
  4. 2017.03.24 [C#] 공용 형식 시스템.
  5. 2017.03.24 [C#] var형식
  6. 2017.03.23 [C#] Object형식, 박싱과 언박싱. 1
  7. 2017.03.23 [C#] 값 형식과 참조형식

[C#] interface

interface


- 추상 클래스와 비슷하다.

- 상속하는 모든 클래스 형식에서는 해당 메소드를 모두 구현해야 한다.

- 직접 인스턴스화할 수 없다.

- 이벤트, 인덱서, 메소드, 프로퍼티만을 가질 수 있다.

- 메소드 구현을 하지 않는다. (선언은 있고 정의는 없다.)

- 클래스와 구조체는 여러 인터페이스를 상속 받을 수 있다.

- 인터페이스 자체는 여러 인터페이스를 상속 받을 수 있다.

- 접근자는 쓸 수 없고 모두 public이다.



사용하는 이유?

- 의도하지 않은 속성,메소드 공개를 막을수 있다.

- OOP의 상속에 억매이지 않고, 구현할 수 있도록 개방성을 제공한다.



※ 주의사항

- 속성, 메소드의 변화 가능성이 많다면, 아예 사용하지 않는것이 좋다.

=> 인터페이스에 변경이 일어난다면, 아주 위험한 상황이 올 수 있다.

인터페이스라는 것이 상속의 원형이 되는 것이므로, 원형이 변경되면

자식들도 변경이 일어난다는 뜻이다. 인터페이스에 경우, 전혀 다른 제품으로의

확장도 지원할 수 있으므로, 인터페이스 설계 시 아주 조심해야 한다.




형태


1
2
3
4
interface 인터페이스이름
{
    메소드
}
cs




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;
 
namespace IUnit
{
    interface IUnit
    {
        void Move(int x, int y);
        void Attack(int x, int y);
        void Die();
    }
 
    class Marine : IUnit
    {
        public void Move(int x, int y)
        {
            Console.WriteLine("아장아장");
        }
 
        public void Attack(int x, int y)
        {
            Console.WriteLine("두두");
        }
 
        public void Die()
        {
            Console.WriteLine("으악");
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            Marine M = new Marine();
            M.Attack(5,5);
            M.Move(55);
            M.Die();
        }
    }
}
cs



IUnit 인터페이스는 구현은 정읳지 않으며, 메소드의 목록만을 제공한다.




[C#] 메소드 오버로딩, 가변길이 매개 변수, 선택적 매개 변수


1. 메소드 오버로딩


하나의 메소드 이름에 여러 개의 구현을 올리는 것.



1
2
3
4
int Plus(int a, int b)
{
    return a + b;
}
cs


int형식의 매개 변수와 반환 형식을 가진 메소드가 있다고 해보자.

그런데, double형식을 지원하는 버전도 추가해야되는 상황이 되었다.


그러면 PlusDouble()이라는 메소드를 새로 만들어줘야 할까?


아니다. 이 때 메소드 오버로딩을 사용하면 Plus()라는 이름을 그대로 사용할 수 있다.

하나의 메소드 이름에 여러 개의 구현을 올리는 메소드 오버로딩이다.



1
2
3
4
5
6
7
8
9
int Plus(int a, int b)
{
    return a + b;
}
 
double Plus(double a, double b)
{
    return a + b;
}
cs


이렇게 오버로딩 해놓으면 컴파일러가 메소드를 호출했을 때,

매개 변수의 수와 형식을 분석해서 어떤 메소드를 호출해야 되는지 찾아준다.


이렇게, 메소드 오버로딩은 이름에 대한 고민을 줄여주는 동시에 코드에 일관성을 유지해준다.



※ 유의사항

- 반환형을 다르게 한다.

- 매개변수의 자료형을 다르게 한다.

- 매개변수의 숫자를 다르게 한다.




2. 가변길이 매개 변수


그저 매개 변수의 "수"가 다르다는 이유만으로 똑같은 메소드를 여러가지 버전으로 오버로딩 해야 할 때가 있다.

이런 경우를 위해 C#은 "가변길이 매개 변수"라는 기능을 제공한다.


가변길이 매개 변수란, 그 개수가 유연하게 변할 수 있는 매개 변수를 말한다.


1
2
3
4
5
6
7
8
int result = 0;
 
result = Sum(1,2);
result = Sum(1,2,3);
result = Sum(1,2,3,4);
result = Sum(1,2,3,4,5);
 
// ...
cs


가변길이 매개 변수는 params 키워드와 배열을 이용해서 선언한다.


1
2
3
4
5
6
7
8
9
10
11
12
int Sum( params int[] array)
{
    int Sum = 0;
    int Count = array.Length;
 
    for(int i = 0; i < Count; i++)
    {
        Sum += array[i];
    }
 
    return Sum;
}
cs


"이게 있으면 메소드 오버로딩은 필요 없네?" 라고 생각할수 있을것이다.

하지만, 매개 변수의 개수가 유한하게 정해져 있다면 가변길이 매개 변수보다는 메소드 오버로딩을 사용하는 것이 적절하다.




3. 선택적 매개 변수


메소드의 매개 변수는 기본값을 가질 수 있다.

매개 변수를 특정 값으로 초기화 하듯 메소드를 선언할 수 있다는 것이다.


1
2
3
4
void MyMethod( int a = 0int b = 0)
{
    Console.WriteLine("{0}, {1}", a, b);
}
cs

결과 : 0, 0


위와같이 기본값을 가지는 매개 변수는 메소드를 호출할 때 데이터 할당을 생략할 수 있다.


1
MyMethod( 3 );
cs

결과 : 3


기본값을 가지는 매개 변수는 필요에 따라 데이터를 할당하거나 할당하지 않을 수 있기 때문에 이를

"선택적 매개 변수"라고 한다.



※ 유의사항

- 선택적 매개 변수는 항상 필수 매개 변수 뒤에 와야 한다.

- 선택적 매개 변수는 메소드 오버로딩과 함께 사용하면 혼란이 야기될수 있으므로 

함께 사용하는 것을 지양한다.


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

[C#] interface  (0) 2017.04.03
[C#] 참조에 의한 매개 변수 전달. ref / 출력 전용 매개 변수. out  (0) 2017.03.24
[C#] 공용 형식 시스템.  (0) 2017.03.24
[C#] var형식  (0) 2017.03.24
[C#] Object형식, 박싱과 언박싱.  (1) 2017.03.23

[C#] 참조에 의한 매개 변수 전달. ref / 출력 전용 매개 변수. out


이런 코드가 있다고 하자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
 
namespace SwapByValue
{
    class MainApp
    {
        public static void Swap(int a, int b)
        {
            int temp = b;
            b = a;
            a = temp;
        }
 
        static void Main(string[] args)
        {
            int x = 3;
            int y = 4;
 
            Console.WriteLine("x:{0}, y:{1}",x,y);
            // 결과 : x:3,y:4
            Swap(x,y);
            
            Console.WriteLine("x:{0}, y:{1}",x,y);
            // 결과 : x:3,y:4
        }
    }
}
cs


우리는 Swap메소드를 사용하므로써 x와 y의 값이 교환 되는 결과를 바라고 있었을 것이다.

그런데 위의 코드에서는 값이 교환되지 않았다.


그럼 x랑 y값에 변환를 주기 위해선 어떻게 해야할까?

바로 ref키워드를 사용하면 된다.

ref키워드를 매개 변수 앞에 붙여주면 된다.




1. 참조에 의한 매개변수 전달.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
 
namespace SwapByValue
{
    class MainApp
    {
        public static void Swap(ref int a, ref int b)
        {
            int temp = b;
            b = a;
            a = temp;
        }
 
        static void Main(string[] args)
        {
            int x = 3;
            int y = 4;
 
            Console.WriteLine("x:{0}, y:{1}",x,y);
            // 결과 : x:3,y:4
            Swap(ref x, ref y);
            
            Console.WriteLine("x:{0}, y:{1}",x,y);
            // 결과 : x:4,y:3
        }
    }
}
cs


우리가 원하는 결과 값이 나왔다는 것을 알 수 있다.


대개의 경우 메소드의 결과값은 하나면 충분하다.

"대개의 경우"에 속하지 않는, 두 개 이상의 결과를 요구하는 메소드는 어떻게 해야할까?


바로 참조에 의한 매개변수 전달을 사용하면 된다.



다음은 나눗셈과 그 나머지를 구하는 코드다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
 
namespace SwapByValue
{
    class MainApp
    {
        public static void Divide(int a, int b, ref int quotient, ref int remainder)
        {
            quotient = a / b;
            remainder = a % b;
        }
 
        static void Main(string[] args)
        {
            int a = 20;
            int b = 3;
            int c = 0;
            int d = 0;
 
            Divide(a, b, ref c, ref d);
 
            Console.WriteLine("a:{0}, b:{1}:, a/b:{2}, a%b:{3}", a,b,c,d);
            // 결과 a:20, b:3, a/b:6, a%b:2
        }
    }
}
cs




2. 출력 전용 매개 변수.


ref을 이용하여 메소드로 부터 여러개의 결과를 얻어올 수 있지만,

C#은 조금 더 안전한 방법으로 똑같은 일을 할수 있게 했다.


out 키워드를 사용한 것이다.


위의 예제 코드에서 ref키워드를 out키워드로 바꿔 사용하면 똑같은 결과를 도출할 수 있다.

그럼 똑같은거 아닌가? 라고 생각할 수 있지만, 그렇지 않다.


out은 ref에게는 없는 안전장치가 있다.


예를들어 ref키워드를 이용해 매개 변수를 넘기는 경우 메소드가 해당 매개 변수에 결과를 저장하지 않아도

컴파일은 아무런 경고를 하지 않는다.


out키워드는 메소드가 해당 매개변수에 결과를 저장하지 않으면 에러 메세지를 출력한다.


물론 초기화 되지 않은 매개변수를 넘기는 것은 가능하다.

왜냐하면 컴파일러가 호출당하는 메소드에서 그 지역 변수에 할당할 것을 보장하기 때문이다.


이렇게 버그를 만들 가능성을 제거할 수 있다면 우리는 그 방법을 사용해야 한다.


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

[C#] interface  (0) 2017.04.03
[C#] 메소드 오버로딩, 가변길이 매개 변수, 선택적 매개 변수  (0) 2017.03.25
[C#] 공용 형식 시스템.  (0) 2017.03.24
[C#] var형식  (0) 2017.03.24
[C#] Object형식, 박싱과 언박싱.  (1) 2017.03.23

[C#] 공용 형식 시스템.


C#은 다양한 종류의 데이터를 다룰수 있다.

byte, int, float, string, object와 같은 다양한 기본데이터 형식을 제공하며,

프로그래머가 이들을 조합해여 복잡한 데이터 형식을 만들어 사용할 수 있다.


또한, C#에서는 스택과 힙이라는 두 가지 메모리 영역을 활용함으로써 변수의 생명주기에 따라 변수를

값 형식이나 참조형식으로 만들어 사용할 수 있다.


이 모든 데이터 형식 체계는 사실 C#의 고유의 것이 아니다.

공용 형식 시스템(Common Type System)이라고 하는 .NET 프레임워크의 형식 체계의 표준을 그대로

따르고 있을 뿐이다.


공용 형식 시스템의 뜻을 풀어보자면 "모두가 함께 사용하는 데이터 형식 체계" 라고 할 수 있다.

C#을 비롯한 .NET 프레임워크를 지원하는 모든언어를 뜻한다.


마이크로소프트가 이러한 시스템을 도입한 이유는 .NET 언어들끼리 호환성을 갖도록 하기 위해서이다.



C#과 C++의 기본 데이터 형식을 살펴 보도록 하자.


클래스 이름 

C# 형식 

 C++ 형식

 비주얼 베이직 형식 

 System.Byte

 byte

 unsigned char

 Byte

 System.SByte

 sbyte

 char

 SByte

 System.Int16

 short

 short

 Short

 System.Int32

 int

 int 또는 long

 Integer

 System.Int64

 long

 __int64

 Long

 System.UInt16

 ushort

 unsigned short

 UShort

 System.UInt32

 uint

 unsigned int   또는 

 unsigned long

 UInteger

 System.UInt64

 ulong

 unsigned __int64

 ULong

 System.Single

 float

 float

 Single

 System.Double double double Double

 System.Boolean

 bool bool Boolean
 System.Char char wchar_t Char
 System..Decimal decimal Decimal Decimal
 System.IntPtr

 없음

 없음

 없음

 System.UIntPtr

 없음 없음 없음
 System.Object object Object*

 Object

 System..String string String* String




공용 형식 시스템의 형식은 각 언어에서 코드에 그대로 사용할수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
 
namespace CTS
{
    class MainApp
    {
        static void Main(string[] args)
        {
            System.Int32 a = 123;
            int b = 456;
 
            Console.WriteLine("a type:{0}, value:{1}", a.GetType().ToString(), a);
            Console.WriteLine("b type:{0}, value:{1}", b.GetType().ToString(), b);
            
            System.String c = "abc";
            string d = "def";
 
            Console.WriteLine("c type:{0}, value:{1}", c.GetType().ToString(), c);
            Console.WriteLine("d type:{0}, value:{1}", d.GetType().ToString(), d);
 
            // 결과
            // a type: System.Int32, value: 123
            // b type: System.Int32, value: 456
            // c type: System.String, value: abc
            // d type: System.String, value: def
        }
    }
}
cs




※ GetType() 메소드 ToString() 메소드


모든 데이터 형식은 object 형식으로부터 상속받는다고 말한적이 있을것이다.

GetType()와 ToString()메소드는

System.Int32와 int, System.String과 string 형식이

object형식으로부터 물려받아 갖고 있는 것이다.


[C#] var형식


C#은 변수나 상수에대해 깐깐하게 형식 검사를 하는 강력한 형식의 언어이다.

하지만, 약한 형식 검사를 지원해주기도 한다. 바로 var라는 키워드 이다.


강력한 형식검사 

- 강력한 형식 검사는 프로그래머의 실수를 줄여주는 장점이 있다.

- 의도치 않은 형식의 데이터를 읽거나 할당하는 일을 막아준다.


약한 형식검사(var)

- 자동으로 해당 변수의 형식을 지정해준다.

- int, long, unit, ulong 같은 형식을 외울 필요가 없다.

- 변수의 선언과 동시에 초기화 해야한다.

- 지역 변수로만 사용할 수 있다.



var키워드를 사용해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
 
namespace UsingVar
{
    class MainApp
    {
        static void Main(string[] args)
        {
            var a = 20;
            Console.WriteLine("Type {0}, Value {1}", a.GetType(), a);
            // 결과 : Type: System.Int32, Value: 20
 
            var b = 3.14;
            Console.WriteLine("Type {0}, Value {1}", b.GetType(), b);
            // 결과 : Type: System.Double, Value: 3.14
 
            var c = "헬로우 월드?";
            Console.WriteLine("Type {0}, Value {1}", c.GetType(), c);
            // 결과 : Type: System.String, Value: 헬로우 월드?
        }
    }
}
cs



배열도 사용 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
 
namespace UsingVar
{
    class MainApp
    {
        static void Main(string[] args)
        {
            var a = new int[] {10,20,30};
            Console.WriteLine("Type {0}, Value: ", a.GetType());
            
            foreach(var e in a)
                Console.Write("{0} ", e)
            Console.WriteLine();
 
            // 결과 : Type: System.Int32[], Value: 10 20 30
        }
    }
}
cs




var형식과 object형식


위의 예제코드를 object형식으로도 작성이 가능하다.

그럼 var와 object는 같은거 아닌가? 라고 생각할수도 있지만.


아니다.


object a = 20;

이라는 코드가 있다고 하자.

컴파일이 실행되면 20은 박싱되어 힙에 들어가고 a는 힙을 가르키게 된다.


var a = 20;은 컴파일러가 a의 형식을 파악한다. 

int a = 20;으로 바꿔 컴파일 시킨다.

컴파일이 실행되면 a를 스택에 올려놓게 된다.


두 개의 형식은 명확히 다른거라고 알아두자.


[C#] Object형식, 박싱과 언박싱.


1. Object형식


object형식은 "상속" 덕분에 어떤 데이터든지 다룰수 있는 데이터 형식이다.


C#은 object가 모든 데이터를 다룰 수 있도록 하기 위해 특별 조치를 취했는데,

모든 데이터 형식이 자동으로 object형식으로부터 상속받게 한 것이다.

다시 말해 object형식이 모든 데이터 형식의 부모라고 하면 된다.


※ 모든 데이터 형식이 가능하다. 프로그래머가 만든 데이터 형식도 마찬가지다.



따라서 컴파일러는 어떤 형식의 데이터라도 object에 담아 처리할 수 있게 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System
 
namespace Object
{
    class Test
    {
        static void Main(string[] args)
        {
            object a = 100;
            object b = 3.14;
            object c = true;
            object d = "헬로우 월드";
            
            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.WriteLine(c);
            Console.WriteLine(d);
        }
    }
}
cs



각 자료형이 처리하는 방식이 다른데 어떻게 이런일이 가능할까?

우리는 그걸 알기 위해 이 뒤에 일어나는 메커니즘을 이해할 필요가 있다.

그 메커니즘을 박싱, 언박싱 이라고 한다.




2. 박싱과 언박싱


object형식은 참조 형식이기 때문에 힙에 데이터를 할당한다.

int나 double 형식은 값 형식이기 때문에 스택에 할당한다.


그런데 앞에서 값 형식의 데이터를 object형식 객체에 담았다.

이 경우는 어느 메모리에 데이터가 할당될까?


object형식은 값 형식의 데이터를 힙에 할당하기 위한 박싱(Boxing)기능을 제공한다.


object형식에 값 형식의 데이터를 할당하려는 시도가 이루어지면 object형식은 박싱을 수행해서 해당 데이터를 힙에 할당한다.


이렇게 박싱이 일어나는 한 편, 힙에 있던 값 형식 데이터를 값 형식 객체에 다시 할당해야 하는 경우가 있다.

다음이 그런 경우다.


1
2
object a = 20;
int b = (int)a;
cs

위 코드에서 a는 20이 박싱되어 저장되어 있는 힙을 참조하고 있다.

b는 a가 참조하고 있는 메모리로부터 값을 복사하려고 하는 중이다.

이 때, 박싱되어 있는 값을 꺼내 값 형식 변수에 저장하는 과정을 언박싱(UnBoxing)이라고 한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
 
namespace BoxingUnBoxing
{
    class MainApp
    {
        static void Main(string[] args)
        {
            int a = 123;
            object b = (object)a;        // a에 담긴 값을 박싱하여 힙에 저장
            int c = (int)b;              // b에 담긴 값은 언박싱하여 스택에 저장
            
            Console.WriteLine(a);        // 결과 : 123
            Console.WriteLine(b);        // 결과 : 123
            Console.WriteLine(c);        // 결과 : 123
        }
    }
}
cs



[C#] 값 형식과 참조형식


값 형식 : 변수가 값을 담는 데이터 형식.

참조 형식 : 변수가 값 대신 값이 있는 곳의 위치(참조)를 담는 데이터 형식.


이 둘을 이해하려면 두 가지 메모리 영역에 대해 알고있어야 한다.

바로 스택(Stack)힙(Heap)이다.


이 메모리 영역 중 값 형식과 관련이 있는 것은 스택 메모리 영역이고,

참조 형식과 관련 있는 것은 메모리 영역이다.



1. 스택과 값 형식.


스택에 대해 이야기 해보자.

스택의 구조는 마치 책상 위에 쌓인 책이라고 생각하면 된다.




맨 밑에 있는 책을 읽고 싶다면, 위에 있는 책들을 모두 걷어낸 후에나 가능할 것이다.

스택 메모리도 이렇게 동작한다. 다음과 같은 코드를 작성했다고 하자.


1
2
3
4
5
{ // 코드 블록 시작
    int a = 10;
    int b = 20;
    int c = 30;
} // 코드 블록 끝
cs



이렇게 선언된 세 변수 a,b,c는 차례대로 스택에 쌓였다가 코드 블록이 끝나면서 스택에서 걷혀 제거된다.

※ 블록을 닫는 괄호 "}"을 만나는 순간 스택에 있는 데이터들이 c,b,a의 순서대로 걷혀진다.



값 형식의 변수는 모두 이 스택에 저장된다.

다시 말해 코드 블록 안에서 생성된 모든 값 형식의 변수들은 중괄호 "}"을 만나면 메모리에서 제거된다.




2. 힙과 참조 형식


메모리 관리가 깔끔한 스택에 반해 힙은 데이터를 스스로 제거할 수 있는 매커니즘을 가지고 있지 않다.

대신 청소부 같은 존재를 고용하고 있는데, 그 청소부의 이름은 가비지 컬렉터(Garbage Collector)이다.

힙에 더 이상 사용하지 않는 객체가 있으면 그 객체를 쓰레기로 간주하고 수거해 가는 기능을 가지고 있다.


그렇다면 왜 굳이 가비지 컬렉터가 필요한 힙 영역을 사용하는 걸까?

스택에 쌓인 데이터들은 코드 블록이 사라지는 시점에서 함께 제거된다.

이것은 스택에 장점이기도 하지만, 동시에 한계이기도 하다.


코드 블록이 끝나는 시점과 상관없이 데이터를 유지하고 싶을 때는 스택의 구조가 발목을 잡는 요소가 된다.


그래서 또 다른 메모리 영역인 힙을 제공하는 것이다.

힙은 코드 블록이 종료되는 것과 관계없이 그 데이터를 게속 유지할 수 있다. 

그리고 이 데이터는 프로그래머가 더 이상 사용하지 않을 때가 됐을 때 가비지 컬렉터가 수거해 제거 한다.


참조 형식의 변수는 힙과 스택을 함께 사용하는데, 힙 영역에는 데이터를 저장하고, 스택 영역에는 데이터가 저장된

힙의 메모리의 주소를 저장한다. 그래서 참조 형식이라는 이름이 붙은 것이다.


참조 형식으로 변수를 선언해보자.

1
2
3
4
{
    object a = 10;
    object b = 20;
}
cs


실제 값 10과 20은 힙 영역에 저장하고, a와 b는 값이 저장된 힙의 주소만 스택에 저장하게 된다.



블록이 끝난 시점에서 스택의 값은 사라진다. 하지만 힙에 남은 값을 사라지지 않는다.



힙의 남은 값은 더 이상 데이터를 참조하는 곳이 없을 때 가비지 컬렉터가 수거해 간다.

prev 1 next