본문으로 바로가기
반응형

지난주에 했던거 복습

객체는 실제로 존재하는것을 말한다.

프로그래밍 언어는 뒤로 나올수록 좋은것을 흡수해서 만들어내서 더 좋음(유리함)

 

문자열

C에서는 char*

자바는 String   클래스

C# string    클래스

기본제공되는것도 있고 기본되지않는것은 사용자가 직접 커스터마이징 가능

class (클래스이름)

{

행위(메서드)

속성(변수)

}

 

문법이 오류없이 컴파일이 되느냐 -> 컴파일 타입 에러 ->(수정하는것을) 컴파일 타입 디버그

실행할때 문제없이 실행되느냐 -> (오류뜨면)런타임 에러

 

클래스를 정의한 것을(붕어빵틀)

실제로 생성한 것을 인스턴스(객체)라고 부름(붕어빵)

틀만 잘 만들면 대량생산이 가능하다

 

 

고구마라는 클래스를 꺼내니 뭔 줄기가 주루루룩 나온다.. 쒯

생성자

종료자

static(변수 , 생성자, 메서드 등등)

static변수, static메서드 : 객체 생성없이 사용가능

static생성자 : 한번만 호출

 

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
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
 
class Person
{
    public string _name;
 
    public Person(string name)
    {
        _name = name;
        Console.WriteLine("일반 생성자");
    }
 
    static Person()
    {
        Console.WriteLine("스태틱 생성자");
    }
    public Person()
    {
        Console.WriteLine("디폴트 생성자");
    }
}
 
namespace _20200601_001
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("시작1");
            Person person1;
            Console.WriteLine("시작2");
            person1 = new Person();
            Console.WriteLine("시작3");
            Person person2 = new Person("");
            Console.WriteLine("시작4");
        }
    }
}
cs

 

 

 

 

class Person을 봤을때,

public Person(string name)으로 문자열 클래스를 선언

static Person()으로 static Person 메서드 선언

public Person()으로 메서드 선언

 

순서

1. writeline으로 시작1출력

2. Person person1;으로 스택에 공간만 생김(출력되지않음)

3. writeline으로 시작2출력

4. person1 = new Person();으로 ->제일 먼저 쓰는 생성자가 static이므로 스태틱생성자 출력->스택의 person1에 new Person으로 힙으로 공간 생성 -> 디폴트생성자 출력 

5. writeline으로 시작3출력

6. Person person2 = new Person("");  에서 스택에 person2생성 -> new Person("")으로 힙에 공간 생성, ""라는 문자열을 받았기때문에 Person(string name)으로 받기때문에 _name=name;으로되고 일반 생성자 출력(static은 한번만 나오므로 출력되지않는다)

7. writeline으로 시작4출력

 

 

stack    Heap

person1   Person

ㅁ     ->    ㅁ

 

person2    Person

ㅁ       ->   ㅁ

0
위의 코드를 디버깅모드로 한줄씩 실행하는 모습

 

생성자 이름은 Person으로 같지만, 인자값으로 구분 -> 생성자 오버로딩도 사용된 것이다.

 

 

 

네임스페이스

메서드의 이름이 겹칠수있기때문에 서로의 공간을 할당해서 중복을 방지할 수 있다.

using을 사용해서 앞에꺼를 생략할 수 있다.

using System  :  System을 생략하겠다.

(System).Cosole.WriteLineI();  -> Console.WriteLine();   으로 쓸 수 있게된다.

 

using을 사용할때 파일의 첫 부분에 있어야하고 어떤 코드도 using문 앞에 와서는 안됨.

 

캡슐화

최소한만 노출하는 기법, 변하지 않는 식이나 값을 고정시켜놓고 

 

 

접근 제한자

private, protected, public, internal, internal protected

 

 

p130

클래스는 상속받을 수 있다, 상속받은 클래스에서 접근가능한 것을 protected(결혼해서 내 방에 애들이 들어올 수 있게 허용해주는거), 

 

 

 

정보 은닉(information hiding)

캡슐화의 수단, 외부에서 이 멤버 변수를 직접 접근할 수 없게 만드는 것임. 클래스 입장에서 "정보"라고 불리는 것은 멤버 변수를 일컫는데 외부에서 이 멤버 변수를 직접 접근할 수 없게 만드는 것이 정보 은닉에 해당한다.

멤버 변수 자체가 클래스의 고유 특성을 반영하고 있다면 외부에서 그 변수의 값에 접근(access)할 필요 있다. 여기서 접근 이라는 단어에는 읽기(rea)와 쓰기(write)라는 두 가지 의미가 있는데, 필드에 읽고 쓰기가 적용될 때는 관례적으로 get과 set이라는 단어를 각각 사용한다. 멤버 변수에 대해 get/set 기능을 하는 메서드를 특별히 접근자 메서드(getter), 설정자 메서드(setter)라고 한다.

 

접근(access) = 접근자 메서드(getter) + 설정자 메서드(setter)

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _20200601_002
{
    class Car
    {
        int iSpeed;
        // get스피드 set스피드 반환타입없는 Speed
        public int GetSpeed()   // iSpeed를 캡슐화시켜났다.
        {
            return iSpeed;
        }
        public void SetSpeed(int _iSpeed)
        {
            if (150<_iSpeed)          // 클래스를 만든사람이 허용범위안에서만 운영하게해서 안전하게 쓸 수 있다.
            {                         // 메서드를 간접적으로 사용하게해라
                _iSpeed = 150;         
            }
            iSpeed = _iSpeed;     
        }
     }
    class Program
    {
        static void Main(string[] args)
        {
            Car MyCar = new Car();
            MyCar.SetSpeed(100); 
            Console.WriteLine("현재 속도 : {0}km/h 입니다", MyCar.GetSpeed());
            MyCar.SetSpeed(300);
            Console.WriteLine("현재 속도 : {0}km/h 입니다", MyCar.GetSpeed());  // 300을 넣었지만 150이 출력
        }
        /*  출력화면
            현재 속도 : 100km/h 입니다
            현재 속도 : 150km/h 입니다
         */
    }
}
 
cs

300을 넣었지만 if문에 걸러져서 150이 출력됬다.

 

 

 

프로퍼티

접근자/설정자(get/set) 메서드를 둬서 접근에 대한 단일 창구를 제공하는 것은 바람직하지만 호출을 위한 메서드 정의를 일일이 코드로 작성하자면 번거롭기때문에 C#에서는 프로퍼티(property)를 제공한다.

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200601_002
{
    class Car
    {
        /*
        int iSpeed;
        // get스피드 set스피드 반환타입없는 Speed
        public int GetSpeed()   // iSpeed를 캡슐화시켜났다.
        {
            return iSpeed;
        }
        public void SetSpeed(int _iSpeed)
        {
            if (150<_iSpeed)         
            {                       
                _iSpeed = 150;        
            }
            iSpeed = _iSpeed;     
        }
        */
        int iSpeed;
        public int Speed // 프로퍼티 사용
        {
            get 
            { 
                return iSpeed;
            }
            set
            {
                if (150 < value)
                {
                    value = 150;
                }
                iSpeed = value;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                /*
             Car MyCar = new Car();
             MyCar.SetSpeed(100); 
             Console.WriteLine("현재 속도 : {0}km/h 입니다", MyCar.GetSpeed());
             MyCar.SetSpeed(300);
             Console.WriteLine("현재 속도 : {0}km/h 입니다", MyCar.GetSpeed()); 
                */
              Car MyCar = new Car();
              MyCar.Speed = 100;
              Console.WriteLine("현재 속도 : 시속 {0}Km", MyCar.Speed);
              MyCar.Speed = 3000;
              Console.WriteLine("현재 속도 : 시속 {0}Km", MyCar.Speed);
 
            }
        }
     }
        /*  
         출력화면
         현재 속도 : 100km
         현재 속도 : 150km
        */
 }
 
cs

 

위의 예제를 프로퍼티로 수정해본 예제

MyCar.Speed = 100;

MyCar.Speed = 3000;    에서 set에 걸쳐서 대입되기때문에 오류가 뜨지 않는다.

 

사용자가 편리하게 쓰게끔 만들었는데 사실 눈속임같은거다, 실제로 메서드 두개를 만들어서 하는거랑 같기때문에 더 편리한걸쓰든 덜편리한걸쓰든 컴파일되서 나오는 출력값은 같으니 상관이없다.

 

 

 

 

상속

코드를 물려받으면 기존의 틀은 유지하고클래스안에서 추가되는 기능을 넣어서 조금 더 나은 코드를 작성할 수 있다.

코드관점에서는 기존의 만들어둔 코드를 땡겨받아서 굳이 또 사용할필요는 없다

 

현실 세계는 어떤 공통적인 특징이 있고 그 특징을 상속(inheritance)받아 다른 세부적인 항목을 정의하는데, 일상적인 많은 객체가 이러한 "계층적"인 관계를 따르고있다. 또 다른 예로 노트북, 데스크탑, 넷북이라는 타입을 정의한다고 해보면 이것들을 공통적으로 부팅, 끄기, 리셋과 같은 행위와 전원이 들어와있는지에 대한 상태값을 제공한다. 이를 정리하면 모두 컴퓨터라는 특징에서 나온 것임을 알 수 있다. 만약 상속이라는 개념이 없다면 노트북, 데스크탑, 넷북 클래스는 아래와 같이 각각 개별적으로 메서드와 상태값을 정의해야만 한다. 

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200601_003
{
    public class NoteBook
    {
        bool powerOn;
        public void Boot()
        {
            Console.WriteLine("컴퓨터 전원을 켭니다.");
        }
        public void Shutdown()
        {
            Console.WriteLine("컴퓨터 전원을 끕니다.");
        }
        public void Reset()
        {
            Console.WriteLine("컴퓨터를 재부팅 합니다.");
        }
    }
    public class Desktop
    {
        bool powerOn;
        public void Boot()
        {
            Console.WriteLine("컴퓨터 전원을 켭니다.");
        }
        public void Shutdown()
        {
            Console.WriteLine("컴퓨터 전원을 끕니다.");
        }
        public void Reset()
        {
            Console.WriteLine("컴퓨터를 재부팅 합니다.");
        }
    }
    public class Netbook
    {
        bool powerOn;
        public void Boot()
        {
            Console.WriteLine("컴퓨터 전원을 켭니다.");
        }
        public void Shutdown()
        {
            Console.WriteLine("컴퓨터 전원을 끕니다.");
        }
        public void Reset()
        {
            Console.WriteLine("컴퓨터를 재부팅 합니다.");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("================= 노트북 =================");
            NoteBook aNoteBook = new NoteBook();
            aNoteBook.Boot();
            aNoteBook.Reset();
            aNoteBook.Shutdown();
            Console.WriteLine("================= 데스크탑 =================");
            Desktop aDesktop = new Desktop();
            aDesktop.Boot();
            aDesktop.Reset();
            aDesktop.Shutdown();
            Console.WriteLine("================= 넷북 =================");
            Netbook aNetbook = new Netbook();
            aNetbook.Boot();
            aNetbook.Reset();
            aNetbook.Shutdown();
        }
    }
}
cs

클래스, 메서드를 일일이 정의하면 코드가 길어질 수 있다

코드가 길어질 수 있기 때문에 상속의 사용이 필요하다. 상속은 기호 ' : (콜론) '으로 사용해서 상속받을 수 있다.(참고로 자바는 extends를 사용) 콜론을 이용해 부모 클래스의 기능을 물려받을 수 있고 실제로 상속받은 클래스는 부모의 속성과 행위를 접근 제한자 규칙에 따라 외부에 제공한다.

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _2020060_004
{
    public class Computer
    {
        bool powerOn;
        public void Boot()
        {
            Console.WriteLine("컴퓨터를 킵니다");
        }
        public void Shutdown()
        {
            Console.WriteLine("컴퓨터를 끕니다");
        }
        public void Reset()
        {
            Console.WriteLine("컴퓨터를 재부팅합니다");
        }
        bool fingerScan; // Notebook 특화 멤버 필드 추가
        public bool HasFingerScanDevice() // Notebook 특화 멤버 메서드 추가
        {
            return fingerScan;
        }
    }
    public class Notebook : Computer
    {      
    }
    public class Desktop : Computer
    {
    }
    public class Netbook : Computer
    {
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("================= 노트북 ===============");
            Notebook aNoteBook = new Notebook();
            aNoteBook.Boot();
            aNoteBook.Reset();
            aNoteBook.Shutdown();
            Console.WriteLine("================= 데스크탑 =============");
            Desktop aDesktop = new Desktop();
            aDesktop.Boot();
            aDesktop.Reset();
            aDesktop.Shutdown();
            Console.WriteLine("================= 넷북 =================");
            Netbook aNetbook = new Netbook();
            aNetbook.Boot();
            aNetbook.Reset();
            aNetbook.Shutdown();
            Console.WriteLine();
        }
    }
}
cs

상속으로 결과는 같은데 코드 줄 수는 줄어든 것을 확인할 수 있다

 

powerOn은 앞에 아무것도 안적어놨으므로 private타입이다.

 

 

 

protected를 달아주니 빨간줄이 없어졌다

어머니의 통장을 쓰려고 했지만 보호가 되어있으니 엄마 통장을 사용할 수가 없다... 그래서 엄마가 통장 공유를 위해 protected로 공유를 해줘야한다.(엄마 통장도 비밀번호가 있다) 

bool powerOn;  -> protected bool powerOn;으로 

 

 

 

흔치 않지만 프로그래밍을 하다보면 상속을 의도적으로 막고 싶을 때도 있다. 우리가 자주 쓰는 string 타입은 상속을 더는 받지 못하도록 제한돼 있는데, 이를 sealed 예약어라고 한다.

 

 

클래스를 선언할때의 규칙이 있다.

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
sealed class Pen
 
{
 
}
 
public class ElectricPen : Pen // 컴파일 오류 발생
 
{
 
}
 
 
 
 
 
class computer{ }
 
class Monitor{ }
 
 
 
class NoteBook : Computer, Monitor  // 컴파일 오류 발생
 
{
 
}
cs

엄마냐 아빠냐 하나만 골라야하는데 두개 고르면 오류가 발생

C#은 단일 상속만 지원한다. C#은 계층상속은 가능하지만 동시에 둘 이상의 부모 클래스로부터 다중 상속을 받는 것은 허용하지 않는다.

 

 

(이제부터 시작이니까 긴장타자는 강사님말씀.)

 

 

객체의 형변환

기본 자료형의 형변환 관계에서 정수 -> int -> short 순으로 일반화 -> 특수화되는 모습을 보인다.

위의 예제에서 Computer는 가장 일반적인 개념으로 그 범위는 Computer를 상속받은 Notebook을 포함한다. 따라서 Notebook(특수화 타입) 인스턴스를 Computer(일반화 타입)의 변수로 대입하는 경우에는 암시적 형변환이 가능하다.

 

 

반대로 부모 클래스(일반화 타입)의 인스턴스를 자식 클래스(특수화 타입)의 변수로 대입하는 것은 암시적 변환이 불가능하다. 물론 강제로 캐스팅 연산자를 사용해 명시적 형변환을 하는 것은 가능하지만 실행하면 오류가 발생한다.

(건물 GPS에서 1층이나 3층이나 위치는 같다는 비유로 말씀해주심)

 

형변환오류일때의 예제

DeviceManager 클래스를 선언하고 Main에서 TurnOff를  선언할때 aNoteBook은 Notebook 객체라 문제없지만 aDesktop은 오류가 뜬다.

 

Notebook device -> Computer device로 상위클래스로 바꿔주면 빨간줄이 없어진다.

 

형변환 예제

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _2020060_006
{
    public class Computer
    {
        protected bool powerOn;
        public void Boot()
        {
            Console.WriteLine("컴퓨터를 킵니다");
        }
        public void Shutdown()
        {
            Console.WriteLine("컴퓨터를 끕니다");
        }
        public void Reset()
        {
            Console.WriteLine("컴퓨터를 재부팅합니다");
        }
        bool fingerScan; 
        public bool HasFingerScanDevice()
        {
            return fingerScan;
        }
    }
    public class Notebook : Computer
    {
    }
    public class Desktop : Computer
    {
    }
    public class Netbook : Computer
    {
    }
    public class DeviceManager
    {
        public void TurnOff(Computer device)
        {
            device.Shutdown();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            /*
            Computer[] machines 
                = new Computer[]  { new Notebook(),    // 배열 선언
                                    new Desktop(),     // 암시적 형변환임
                                    new Netbook() };  
            DeviceManager aDevicemanager = new DeviceManager();
            */
            
            // 위에 주석처리된 배열구문 풀어서 쓴거
            Computer[] machines = new Computer[3];   // 배열 3칸 만들기
            machines[0= new Notebook();
            machines[1= new Desktop();
            machines[2= new Netbook();
            DeviceManager aDevicemanager = new DeviceManager();
                                     // 참고로 int배열은 int[] iNum = new int[3];  이 끝이다
                                     // Computer[] machines = new Computer[3];  랑 똑같은거임
 
            foreach (Computer Temp in machines)     // foreach문으로 배열개수만큼 돌릴 수 있다
             {
                  aDevicemanager.TurnOff(Temp); 
             }
            /*
             출력 결과
             컴퓨터를 끕니다
             컴퓨터를 끕니다
             컴퓨터를 끕니다
             */
        }
    }
}
cs

 

 

 

 

 

 

as, is 연산자

실행중간에 변환이 가능한지를 알아내는 연산자, 형변환이 불가능하면 null을 반환하기때문에 null 반환에 따른 코드로 형변환에 대해 설명할 수 있다.(시간이 부족해서 예제에는 is 연산자만 실습했다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
    {
        static void Main(string[] args)
        {
            int n = 5;
            if(!(n is string))  // n이 string형이냐? (!는 반대연산자)
            {
                Console.WriteLine("변수 n은 string 타입이 아닙니다.");
            }
 
            string txt = "text"
            if(false == (txt is int))   // 변수 txt가 int형이냐?
            {
                Console.WriteLine("변수 txt는 int 타입이 아닙니다");
            }
 
        }
    }
cs

 

 

 

반응형