본문으로 바로가기
반응형

 

지난 시간꺼 복습

상속 :  기호로 콜론 ' : ' 를 사용, 슈퍼 클래스의 정보를 가져와서 쓸 수 있고, 자기 기능도 쓰면서 더 업그레이드된 코드를 사용할 수 있다.

상속의 장점

1. 코드의 재사용 : 시간을 아낄 수 있다->비용감소

2. 검증된 코드를 사용하는거임 ->안정적임

상속의 단점

각종 파생된 일들이 복잡하게 발생한다

 

 

object <- A <- B <- C

C Object는 CBA갖고있다

B Object는 BA 갖고있다

A Object는 A갖고있다

 

A aRef  // A라는 참조(레퍼런스) 변수 aRef를 만들면

B bRef

C cRef

 

A Object

B Object

C Object   

셋 중에 A가 가장 범용성이 높다

 

 

as / is 연산자

ㅁ as ㅇ       // ㅇ이 ㅁ의 형(int, char같은거)이냐? 아니면 NULL 반환

ㅁ is ㅇ       // ㅇ이 ㅁ의 소속이냐?(불린형)

 

C object as A object    => 가능할 것 같음

B object is A object    => 참

 

지난번 예제에서

Notebook notebook = pc as Notebook;  // pc는 컴퓨터라서 형이 다르다 -> 거짓이므로 Null이 됨 

 (결과)                   = Null

 

Data Type (자료형)은 크게 값, 참조로 두가지로 나뉜다

1. 값  : 기본형, 구조체(struct로 적어서 사용자가 만들어쓰기때문에 사용자 정의 타입이라고도 부름), enum       

2. 참조 : 객체의 모든것      

 

-> as / is는 기본형(값타입에)에 쓸 수 없다.  => 객체를 비교하는 것이다,

 

 

 

 

조상 클래스:system.object

상속안받으면 자동으로 상속시킴

C# 모든 클래스는 다 상속받은거임, 우리가 명시하면 그거 상속받은거고 명시안하면 무언가에게 상속받음

object가 C#에서 정의되는 모든 클래스의 부모이다.(최상위 부모 클래스임)

 

 

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
41
42
43
44
45
46
47
48
49
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200602_001
{
    class Program
    {
     class A
    {
    }
    
    class B : A
    {
    }
    
    class C : B
    {
    }
        static void Main(string[] args)
        {
            A aObj = new A();
            B bObj = new B();
            C cObj = new C();
            
            A aRef;
            B bRef;
            C cRef;
 
            // 퀴즈1 : aRef가 가리킬 수 있는 객체는 모두 몇 개 인가?
            aRef = aObj;
            aRef = bObj;
            aRef = cObj;
            // 퀴즈2 : bRef가 가리킬 수 있는 객체는 모두 몇 개 인가?
            bRef = bObj;
            bRef = cObj;
            // 퀴즈3 : cRef가 가리킬 수 있는 객체는 모두 몇 개 인가?
            cRef = cObj;
 
            object oRef;
            oRef = aObj;
            oRef = bObj;
            oRef = cObj;
        }
    }
}
 
cs

aRef는 만능 레퍼런스이고, object는 최상위 클래스이다. 

C는 A의 클래스지만 A는 C의 클래스가 아니기때문에 C에서 A객체 코드를 작성하면 컴파일 오류가 뜬다

 

object는 최상위 클래스라 어떤 객체를 넣어줘도 오류가 뜨지않는다
위의 코드예제에서 class A, class B, class C는 안의 내용이 아무것도 없는데도 4가지의 기능이 들어가있다

 

 

 

object에는 4가지 메서드가있다.

 

1. Tostring(문자열 반환)

해당 인스턴스가 속한 클래스의 전체 이름(FQDN)을 반환한다.

만들어서 쓰는게 편하고 고오급 기술에 속함.

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
 
namespace _20200602_001
{
    class A
    {
    }
    class B : A
    {
    }
    class C : B
    {
    }
    class Program
    {
        static void Main(string[] args)
        {
            A aObj = new A();
            B bObj = new B();
            C cObj = new C();
            
            A aRef;
            B bRef;
            C cRef;
            
            Console.WriteLine(aObj.ToString());
            Console.WriteLine(bObj.ToString());
            Console.WriteLine(cObj.ToString());
        }
    }
}
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
class A
{
}
class B : A
{
}
class C : B
{
}
namespace _20200602_001
{
    
    class Program
    {
        static void Main(string[] args)
        {
            A aObj = new A();
            B bObj = new B();
            C cObj = new C();
            
            A aRef;
            B bRef;
            C cRef;
            
            Console.WriteLine(aObj.ToString());
            Console.WriteLine(bObj.ToString());
            Console.WriteLine(cObj.ToString());
        }
    }
}
cs

 

위의 코드에서 클래스 ABC의 위치에 주목해보자

네임스페이스 안에넣으면 네임스페이스+A

네임스페이스 밖에넣으면 ABC가 출력됨

 

 

 

 

문자열 500;

정수 500;

각 500에 +1을 했을때 문자열 500은 + 1 할 수 없다.

 

아스키코드 숫자를 보면

0은 48

1은 49

.

.

5는 53

.

.

9는 57

 

한글자씩 출력하는데 5의 아스키코드는 53이다. 이때 아스키코드 53을 받아서 화면에 5를 출력된다.

(참고로 VS에서 Alt누르고 숫자누르면 아스키코드가 입력됨(ex. alt+48  누르면 0이 나옴))

 

실제로 C#에서 값을 입력하면 그 값이 해당되는 값을 출력할 수 있다.(값을 받으면 아스키코드에 해당되는 값을 출력하는 것이다)

1
2
3
4
5
6
7
8
9
10
11
12
static void Main(string[] args)
        {
            int n = 500;
            double d = 3.14123123141245;
 
            string txt = "Hello: ";
 
            Console.WriteLine(txt + n.ToString());
            Console.WriteLine(txt + d.ToString());
 
            txt = d.ToString();
}
cs

해당 객체를 문자열로 바꿔준다.(기본형도 문자열형태로 바꿔준다)

 

 

2. GetType

겟타입 메서드는 클래스에대해 타입의 전체 이름을 반환하는 수단을 제공한다. 개발자가 class로 타입을 정의하면 내부적으로 해당 class 타입의 정보를 가지고 있는 System.Type의 인스턴스를 보유하게되고, 바로 그 인스턴스를 가져올 수 있는 방법이 GetType 메서드를 통해 제공된다.

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200602_001
{
class A
    {
    }
    class B : A
    {
    }
    class C : B
    {
    }
class Program
     {
        static void Main(string[] args)
        {
            C c = new C();            Type type = c.GetType();
 
            Console.WriteLine(type.FullName);  // 풀네임 호출
            Console.WriteLine(type.IsClass);   // 니가 클래스니?
            Console.WriteLine(type.IsArray);   // 니가 배열이니?
            Console.WriteLine();  
   }
  }
}
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
 
namespace _20200602_001
{
class Program
    {
        static void Main(string[] args)
        {
            int n = 5;
            string txt = "text";
 
            Type intType = n.GetType();
 
            Console.WriteLine(intType.FullName);
            Console.WriteLine(txt.GetType().FullName);
            Console.WriteLine();
   }
  }
}
cs

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
 
namespace _20200602_001
{
class Program
    {
        static void Main(string[] args)
        {
Type type = typeof(double);
            Console.WriteLine(type.FullName);
            Console.WriteLine(typeof(System.Int16).FullName);
            Console.WriteLine();
}
    }
}
cs

 

 

 

3. Equals

값을 비교한 결과를 불린형으로 반환한다.

값 형식이 하나인 int 타입은 변수가 가리키는 값 자체를 대상으로 결괏값을 반환한다. 근데 class로 생성한 참조 형식은 어떨까?

 

 

 동일한 값을 소유한 참조형식에서 Equals 메서드는 False를 반환한다. 왜냐하면 "힙에 할당된 데이터 주소를 가리키고있는 스택 변수의 값"을 비교하기 때문인데, new Book으로 생성된 힙 메모리의 위치가 다르기 때문에 그 안에 들어간 값이 어떤 것이든 상관없이 Equals 메서드는 False를 반환하게 된다.

 

 

이퀄즈는 같은 객체인지 비교한다. 위의 Book의 정보는 같지만 객체는 다르다. 데이터를 비교하는게아니고 동일한 객체인지 비교하는 것이기때문이다.

쌍둥이가 있더라도 1명이 아니라 2명인데 다른 사람임(쌍둥이 둘이 영화관가면 1명만 돈내는게 아닌것처럼)

 

 

 

 

 

 

4. GetHashCode(1:1 대응)

GetHashCode 메서드는 특정 인스턴스를 고유하게 식별할 수 있는 4바이트 int값을 반환한다. Equals의 반환값이 True인 객체라면 서로 같음을 의미하고, 그 객체들의 고윳값 또한 같아야한다. 그러나 Equals 반환값이 False라면 GetHashCode의 반환값도 달라야 한다. 이 때문에 Equals 메서드를 하위 클래스에서 재정의하면 GetHashCode까지 재정의하는데, 이를 따르지 않으면 컴파일 경고가 발생한다.

 

객체 생성마다 고유번호를 부여해서 같은지 비교해서 Equals로 고유번호를 비교해서 고유번호가 같으면 같은 객체, 다르면 다른 객체로 판별한다.

 

 int 타입의 경우 1:1 매핑을 할 수 있다. 예로 long 타입은 범위가 더 크므로 경우에 따라 해시 충돌(hash collision)이 발생할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
        static void Main(string[] args)
        {
            short n1 = 256;            
            short n2 = 32750;
            short n3 = 256;
 
            Console.WriteLine(n1.GetHashCode());
            Console.WriteLine(n2.GetHashCode());
            Console.WriteLine(n3.GetHashCode());
 
            Book book1 = new Book(9788998139018);
            Book book2 = new Book(9788998139018);
 
            Console.WriteLine(book1.GetHashCode());
            Console.WriteLine(book2.GetHashCode());    
        }
 
cs

 

 

배열의 조상 : System.Array

소스코드에 정의되는 배열은 모드 Array 타입을 상속받는다.

멤버 타입 설명
Rank 인스턴스 프로퍼티 배열 인스턴스 차원 수를 반환
Length 인스턴스 프로퍼티 배열 인스턴스 요소 수를 반환
Sort 정적 메서드 배열 요소의 값을 순서대로 정렬
GetValue 인스턴스 메서드 지정된 인덱스의 배열 요소 값 반환
Copy 정적 메서드 배열의 내용을 다른 배열에 복사
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
41
42
43
class Program
    {
        private static void OutputArrayInfo(Array arr)
        {
            Console.WriteLine("배열의 차원 수: " + arr.Rank); //  Rank 프로퍼티
            Console.WriteLine("배열의 요소 수: " + arr.Length);   //  Length 프로퍼티
            Console.WriteLine();
        }
 
        private static void OutputArrayElements(string title, Array arr)
        {
            Console.WriteLine("[" + title + "]");
 
            for (int i = 0; i < arr.Length; i++)
            {
                Console.Write(arr.GetValue(i) + ", ");  //  GetValue 인스턴스 메서드
            }
 
            Console.WriteLine();
            Console.WriteLine();
        }
        static void Main(string[] args)
        {
            long A = 0x1234567801ABCDEF;
            int B = 0x01ABCDEF;
 
            bool[,] boolArray = new bool[,] { { truefalse }, { falsefalse } };
            OutputArrayInfo(boolArray);
 
            int[] intArray = new int[] { 543210 };
            OutputArrayInfo(intArray);
 
            OutputArrayElements("원본 intArray", intArray);
            Array.Sort(intArray);   //  Sort 정적메서드
            OutputArrayElements("Array.Sort 후 intArray", intArray);
 
            int[] copyArray = new int[intArray.Length];
            Array.Copy(intArray, copyArray, intArray.Length);   //  Copy 정적 메서드
 
            OutputArrayElements("intArray로부터 복사된 copyArray", copyArray);
 
        }
    }
cs

 

 

this

자기자신 객체를 가리키는 방법, 지금까지 클래스 내부에서 멤버에 접근했을 때 this를 생략했다고 봐도 무방하다. 그래도 굳이 코드를 길게해서 사용하는것은 개인의 취향이다. 생성자를 여러개 오버로딩 할 경우 모든 변수값을 받는 하나의 생성자를 만들어두고 this를 쓰면 중복코드를 상당히 줄일수 있다. 하지만, this는 new로 할당된 내부 객체를 가리키는 내부 식별자이므로 클래스에서 정의된 정적멤버는 this 예약어를 사용할 수 없다.

 

예제) 여러개의 생성자 오버로딩 코드 줄이기

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200602_004
{
    class Car
    {
        int iSpeed;      // 속도
        string sColor;   // 색상
        string sName;    // 이름
        string sVender;  // 제조사
        public void Print()
        {
            Console.WriteLine("속  도 : {0}", iSpeed);
            Console.WriteLine("생  상 : {0}", sColor);
            Console.WriteLine("이  름 : {0}", sName);
            Console.WriteLine("제 조 사 : {0}", sVender);
            Console.WriteLine();
        }
        public Car()  // 디폴트 생성자
        {
            iSpeed = 0;
            sColor = "화이트";
            sName = "미정";
            sVender = "미정";
        }
        public Car(int iSpeed) 
        {
            this.iSpeed = iSpeed;  // 자기자신에게 넣고싶은데 컴파일 오류가 뜨는데 this를 적어주면 해결된다,
            sColor = "화이트";
            sName = "미정";
            sVender = "미정";
        }
        public Car(string sColor)
        {
            iSpeed = 0;
            this.sColor = sColor;
            sName = "미정";
            sVender = "미정";
        }
        public Car(string sColor, string sName)
        {
            iSpeed = 0;
            this.sColor = sColor;
            this.sName = sName;
            sVender = "미정";
        }
        public Car(string sColor, string sName, string sVender)
        {
            iSpeed = 0;
            this.sColor = sColor;
            this.sName = sName;
            this.sVender = sVender;
        }
        public Car(int iSpeed, string sColor, string sName, string sVender)
        {
            this.iSpeed = iSpeed;
            this.sColor = sColor;
            this.sName = sName;
            this.sVender = sVender;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Car aCar = new Car();
            aCar.Print();
 
            aCar = new Car(80);
            aCar.Print();
 
            aCar = new Car("빨강");
            aCar.Print();
 
            aCar = new Car("빨강""그랑죠");
            aCar.Print();
 
            aCar = new Car("빨강""그랑죠""현다이");
            aCar.Print();
        }
    }
}
 
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200602_004
{
    class Car
    {
        int         iSpeed;     // 속도
        string      sColor;     // 색상
        string      sName;      // 이름
        string      sVender;    // 제조사
 
        public void Print()
        {
            Console.WriteLine("속  도 : {0}", iSpeed);
            Console.WriteLine("색  상 : {0}", sColor);
            Console.WriteLine("이  름 : {0}", sName);
            Console.WriteLine("제조사 : {0}", sVender);
        }
        public Car() : this(0"화이트""미정""미정")
        {
        }
        public Car(int iSpeed) : this(iSpeed, "화이트""미정""미정")
        {
        }
        public Car(string sColor) : this(0, sColor, "미정""미정")
        {
        }
        public Car(string sColor, string sName) : this(0, sColor, sName, "미정")
        {
        }
        public Car(string sColor, string sName, string sVender) : this(0, sColor, sName, sVender)
        {
        }
        public Car(int iSpeed, string sColor, string sName, string sVender)
        {
            this.iSpeed = iSpeed;
            this.sColor = sColor;
            this.sName = sName;
            this.sVender = sVender;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Car aCar = new Car();
            aCar.Print();
 
            aCar = new Car(80);
            aCar.Print();
 
            aCar = new Car("빨강");
            aCar.Print();
 
            aCar = new Car("빨강""그랑죠");
            aCar.Print();
 
            aCar = new Car("빨""그냥죠""현다이");
            aCar.Print();
 
            aCar = new Car(100"빨캉""그냥죠""현다이");
            aCar.Print();
        }
    }
}
cs

두 코드를 비교해보면 Car 메서드를 생성할때마다 비슷한 뉘앙스의 코드들이 눈에띈다. 이때 this 예약어를 사용함으로써 비슷한 코드를 하나의 메서드 내에서(: this()를 붙여줌으로써) 비교적 간단하게 코드를 짤 수 있다.

 

 

 

base

엄마/아빠찬스

public EBook 생성자 정의에서 오류가 발생한다. 자식 클래스를 생성한다는 것은 곧 부모 클래스의 생성자도 함꼐 호출한다는 의미이기 때문이다. 

 

부모 클래스를 만들때 private로 소유하고 있는 멤버를 초기화할 수 있지만 자식 클래스는 부모 클래스의 private멤버에 접근 할 수 없으므로(엄마 통장을 털수없으므로) 초기화가 불가능하다.

 

위의 예제에서 오류가 발생하는 이유는 자식 클래스가 생성되는 시점에 부모 클래스의 생성자를 호출해야하는데 "기본 생성자"가 부모 클래스에는 없기 때문이다. 

 

부모클래스에서 제공되는 Book(decimal isbn13) 생성자를 C# 컴파일러가 자동으로 연계해 줄 수는 없다. 왜냐면 isbn13값을 넣어주어야하는데 어떤 값을 넣어야할지 컴파일러 입장에서는 알 수 없을뿐더러 부모 클래스의 생성자가 여러 개 있는 상황에서는 어떤 생성자를 자동으로 호출해야 할지도 모호하다. 

 

바로 이런 경우에 base 예약어를 이용해 어떤 생성자를 어떤 값으로 호출할지 명시해서 문제를 해결할 수 있다.

 

 

 

 

 

 

다형성

메서드 오버라이드

 

부모한테있는 메서드를 완전히 똑같게 새로 만드는 것을 오버라이드라고한다.

Move라는 메서드를 통해 포유류의 움직임을 정의해보자(왼쪽코드). 근데 자식 객체들이 움직이는 방법이 각각 다르다는 문제가 있다. 즉, 네발, 수영, 두발로 움직이는 점이다. Move라는 특징은 공유하지만 자식들의 행동방식이 다르므로 각자 움직임을 재정의해야하는데 이를 코드로 표현하면 오른쪽코드와 같다. 

 

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200602_006
{
    class Mammal
    {
        public void Move()
        {
            Console.WriteLine("이동한다");
        }
    }
    class Lion : Mammal
    {
        public void Move()
        {
            Console.WriteLine("네 발로 움직인다.");
        }
    }
    class Whale : Mammal
    {
        public void Move()
        {
            Console.WriteLine("수영한다.");
        }
    }
    class Human : Mammal
    {
        public void Move()
        {
            Console.WriteLine("두 발로 움직인다");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Mammal one = new Mammal();
            one.Move();
 
            Lion lion = new Lion();
            lion.Move();
 
            Whale whale = new Whale();
            whale.Move();
 
            Human human = new Human();
            human.Move();
        }
    }
}
 
cs

 

 

 

메서드 오버라이드까지했는데 강의시간이 부족해서 급하게 끝나버렸다.. 다음은 보충설명과 오버라이드, base를 이용해서 메서드를 재사용하는거부터 공부해보자

반응형