7장. 클래스: this 키워드, this() 생성자, 접근 한정자, static 변수, 구조체
주의) 생성자나 종료자 개념 등 기본적인 것들은 생략하였습니다.
클래스가 뭐야?
클래스란 int, string과 같이 하나의 형식이라고 생각하면 된다.
인스턴스는 클래스라는 붕어빵 틀로 찍어낸 각각의 붕어빵이라고 생각하면 쉽다.
생성자
class Cat
{
// 생성자 자동 생성
}
생성자를 따로 명시하지 않을 경우 기본적으로 생성된다.
정적 필드와 메소드 선언, static
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestConsoleApp_4._19
{
class Cat
{
public static int Speed = 10;
public string Name = "";
int _leg = 4;
public Cat()
{
this.Speed += 10;
// Speed += 10; // 과 동일, this 생략 가능
}
public Cat(string name) : this() // Cat() 호출
{
this.Nmae = name;
}
}
// 이하생략
}
지역 변수는 보통 변수명 앞에 _(언더바)를 붙임
this 키워드
클래스에서 함수 정의 시 필드(내부 변수)에 this 키워드는 생략 가능합니다.
this() 생성자
this() 생성자는 자기 자신의 생성자를 가리킵니다.
예제에서 MyCat()은 Speed를 올리고,
MyCat(string)은 Name을 설정합니다.
Speed를 올리는 것은 MyCat()을 호출하여 처리했습니다.
this()는 생성자에서만 사용할 수 있습니다.
C# 클래스 6 - this 키워드, this() 생성자
this 키워드 this는 객체가 자신을 지칭할 때 사용하는 키워드입니다. 클래스 내부에서 필드명과, 메서드의 매개 변수의 이름이 동일할 때this 키워드로 모호성을 제거할 수 있습니다.this가 붙은 변
qzqz.tistory.com
접근 한정자란?
public | 설명생략 | internal | 같은 어셈블리 언어 내에서의 public |
protected | 설명생략 | protected internal | 같은 어셈블리 언어 내에서의 protected |
private | 설명생략 | private protected | 같은 어셈블리 언어 내에서의, 상속받은 클래스 내부에서만 접근 가능 |
sealed: 오버라이딩 봉인
분할 클래스: 클래스의 구현이 길어질 경우 여러 파일에 나눠서 구현 (소스코드 관리의 편의 제공에 그침)
부모 객체에 접근하는 키워드, Base
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestConsoleApp_4._19
{
class Animal
{
public string Name;
public string Color;
protected int Age;
public Animal(string Name, string Color)
{
this.Name = Name;
this.Color = Color;
}
protected void Sing()
{
Console.WriteLine("Sing~");
}
}
class Cat : Animal
{
public static int Speed = 10;
public Cat(): base("", "") { }
public Cat(string Name, string Color): base(Name, Color) { }
public Cat(string Name, string Color, int Age): base(Name, Color)
{
this.Age = Age;
}
~Cat()
{
Console.WriteLine($"${this.Name}: 종료...");
}// 종료자 - 사용하지 않는 것이 좋음
}
// 생략
}
Base 키워드를 통해 부모 클래스에 접근할 수 있습니다.
base()는 기반 클래스의 생성자입니다.
https://slaner.tistory.com/124
base 키워드를 알아보자
안녕하세요!정말 오랜만의 글입니다! 이 글에서는 C#의 base 키워드에 대해서 알아보려고 합니다. base 키워드해당 키워드를 사용하는 클래스의 부모 클래스를 가리키는 것일단, 코드를 보시겠습
slaner.tistory.com
기반 클래스 <=> 파생 클래스, is와 as 키워드
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestConsoleApp_4._19
{
class Animal
{
public string Name;
public string Color;
protected int Age;
public Animal(string Name, string Color)
{
this.Name = Name;
this.Color = Color;
}
protected void Sing()
{
Console.WriteLine("Sing~");
}
}
class Cat : Animal
{
public static int Speed = 10;
public Cat(): base("", "") { }
public Cat(string Name, string Color): base(Name, Color) { }
public Cat(string Name, string Color, int Age): base(Name, Color)
{
this.Age = Age;
}
~Cat()
{
Console.WriteLine($"${this.Name}: 종료...");
}// 종료자 - 사용하지 않는 것이 좋음
public void Meow()
{
Console.WriteLine($"{this.Name}: 야옹");
base.Sing();
}
}
class Program
{
static void Main(string[] args)
{
Animal animal = new Cat();
if (animal is Cat) { // is: 해당 형식에 해당하는지 여부 반환
Cat cat7 = animal as Cat; // as: 참조 형식에 대하여 형변환하며, 실패하는 경우 null 반환
cat7.Meow();
}
}
}
}
is 키워드: 해당 형식에 해당하는지의 여부(bool) 반환
as 키워드: 참조 형식에 대하여 형 변환, 실패 시 null 반환
animal(Cat의 인스턴스)이 Cat에 해당하면,
Animal에 담긴 animal을 Cat으로 형 변환 (다운캐스팅)
업캐스팅과 다운캐스팅
https://bonjenny.tistory.com/16
[C#] 업캐스팅과 다운캐스팅
https://see-ro-e.tistory.com/133 [C# 때려잡기] C# 강의 30. 다형성, 다운 캐스팅 업 캐스팅 기존에 내 캐릭터에 칼을 가지도록 하였다. class Weapon { public void Attack() { Console.WriteLine("무기로 공격!"); } } class Kni
bonjenny.tistory.com
static이란?
여러 개의 인스턴스가 하나의 부모 클래스 static 변수를 공유하고 있는 것이다.
예를 들자면 인스턴스의 성 같은 것이다.
static 변수가 아닌 인스턴스 변수는 인스턴스의 이름 같은 것이다.
// static O
class Person {
static string 성 = "엄";
}
Person1 = new Person() {
Person.성 = "김";
} // 모든 Person의 인스턴스의 성이 "김"으로 바뀜
Person2 = new Person() {
Person.성 = "박";
} // 모든 Person의 인스턴스의 성이 "박"으로 바뀜
// static X
class Person {
string 성 = "엄";
}
Person1 = new Person() {
Person1.성 = "김";
} // Person1 인스턴스의 성이 "김"으로 바뀜
Person2 = new Person() {
Person2.성 = "박";
} // Person2 인스턴스의 성이 "박"으로 바뀜
정확하지 않은 문법이니 참고용으로만 봐주세요.
오버라이드와 다형성
class Animal
{
public string Name;
public string Color;
protected int Age;
public Animal(string Name, string Color)
{
this.Name = Name;
this.Color = Color;
}
public virtual void Sing()
{
Console.WriteLine("Sing~");
}
}
class Cat : Animal
{
public static int Speed = 10;
public Cat(): base("", "") { }
public Cat(string Name, string Color): base(Name, Color) { }
public Cat(string Name, string Color, int Age): base(Name, Color)
{
this.Age = Age;
}
public void Meow()
{
Console.WriteLine($"{this.Name}: 야옹");
base.Sing();
}
public override void Sing()
{
Console.WriteLine("Cat Sing~");
}
}
다형성(Polyorphism)이란?
다형성: 객체가 여러 형태를 가질 수 있음을 의미
즉, 자신으로부터 상속받아 만들어진 파생 클래스를 통해 다형성을 실현
Animal 클래스의 Sing 메소드를 업그레이드한 Cat 클래스의 Sing 메소드
오버라이딩을 위한 조건, virtual 키워드
virtual 키워드: 오버라이딩을 할 메소드가 virtual 키워드로 한정되어있어야 함
오버라이딩 정의, override 키워드
override 키워드: 재정의한 메소드 앞에 붙임, 메소드를 재정의하고있음을 컴파일러에게 알리는 역할
메소드 숨기기, sealed 키워드
sealed class Animal { } // 상속 불가
sealed 키워드: 해당 클래스의 상속을 불가하도록 만듭니다.
오버라이딩한 메소드는 파생 클래스의 파생 클래스에서도 자동으로 오버라이딩이 가능하므로, 브레이크를 걸어주기 위해 sealed 메소드가 필요한 것!
new 키워드로 메소드 숨기기? 오버라이드랑 다를게 뭐야?
class Animal
{
public string Name;
public string Color;
protected int Age;
public Animal(string Name, string Color)
{
this.Name = Name;
this.Color = Color;
}
public virtual void Sing()
{
Console.WriteLine("Sing~");
}
public void Sing2()
{
Console.WriteLine("Sing2~");
}
}
class Cat : Animal
{
public static int Speed = 10;
public Cat(): base("", "") { }
public override void Sing()
{
Console.WriteLine("Cat Sing~");
}
public new void Sing2()
{
Console.WriteLine("Cat Sing2~");
}
}
호출은 다음과 같이 할 수 있다.
Cat cat = new Cat();
cat.Sing2(); // Cat Sing2~ 출력
그러나 오버라이딩과 다른 점은, 다음과 같이 객체를 선언하면 Animal 버전의 Sing2() 메소드가 그대로 노출된다는 것이다.
Animal cat = new cat();
cat.Sing2(); // Sing2~ 출력
따라서 메소드 숨기기는 완전한 다형성을 구현하는 방법이 아니다. new보다는 override를 이용하자.
오버라이딩 봉인, sealed override
class Animal
{
public string Name;
public string Color;
protected int Age;
public Animal(string Name, string Color)
{
this.Name = Name;
this.Color = Color;
}
public virtual void Sing()
{
Console.WriteLine("Sing~");
}
}
class Cat : Animal
{
public static int Speed = 10;
public Cat(): base("", "") { }
public sealed override void Sing()
{
Console.WriteLine("Cat Sing~");
}
}
class KoreanCat: Cat
{
//public override void Sing() // Error: 오버라이딩 불가
//{
// Console.WriteLine("Cat Sing~");
//}
}
virtual 로 선언된 Animal 클래스의 Sing() 메소드를 Cat 클래스가 오버라이드하여 구현하였다.
그러나 이후로 sealed override로 막아놓았기 때문에 KoreanCat 클래스에서는 오버라이딩이 불가하다.
Animal 클래스에서는 virtual 키워드로 이미 추상화가 가능하다고 선언해놓았기 때문에, 오버라이딩을 막는 키워드가 존재하지 않아도 괜찮다.
중첩 클래스와 분할 클래스
분할 클래스
단순히 개발의 편의성을 위해 여러 번에 나눠서 구현하는 클래스
중첩 클래스
Home home;
public void MakeHome()
{
this.home = new Home();
}
public void Rest()
{
this.home?.Cure(this);
}
class Home
{
public void Cure(Cat cat)
{
cat.Age -= 1;
}
}
중첩클래스는 자신이 소속되어있는 클래스의 멤버 (private 멤버에까지도!) 에 자유롭게 접근할 수 있다.
중첩 클래스를 사용하는 이유
1. 클래스 외부에 공개하고 싶지 않은 형식을 만들고자 할 때
2. 현재의 클래스 일부분처럼 표현할 수 있는 클래스를 만들고자 할 때
확장 메서드
확장 메서드: 기존 클래스의 기능을 확장하는 기법
static class CatExtension
{
public static void Hug(this Cat cat)
{
Console.WriteLine($"{cat.Name}이 안아 줍니다.");
}
}
이렇게 선언하면 Hug가 마치 본래 Cat의 메소드였던것처럼 사용할 수 있습니다.
클래스와 구조체
\ | 클래스 | 구조체 |
키워드 | class | struct |
형식 | 참조형식 | 값형식 |
복사 | 얕은 복사 | 깊은 복사 |
인스턴스 생성 | new 연산자, 생성자 | 선언 |
생성자 | 매개변수 없는 생성자 선언 가능 | 매개변수 없는 생성자 선언 불가능 |
상속 | 가능 | 모든 구조체는 Object를 상속받는 ValueType을 상속받음 |
사용한 코드
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestConsoleApp_4._19
{
class Animal
{
public string Name;
public string Color;
protected int Age;
public Animal(string Name, string Color)
{
this.Name = Name;
this.Color = Color;
}
public virtual void Sing()
{
Console.WriteLine("Sing~");
}
public void Sing2()
{
Console.WriteLine("Sing2~");
}
}
class Cat : Animal
{
public static int Speed = 10;
public Cat(): base("", "") { }
public Cat(string Name, string Color): base(Name, Color) { }
public Cat(string Name, string Color, int Age): base(Name, Color)
{
this.Age = Age;
}
~Cat()
{
Console.WriteLine($"${this.Name}: 종료...");
}// 종료자 - 사용하지 않는 것이 좋음
public void Meow()
{
Console.WriteLine($"{this.Name}: 야옹");
base.Sing();
}
public sealed override void Sing()
{
Console.WriteLine("Cat Sing~");
}
public new void Sing2()
{
Console.WriteLine("Cat Sing2~");
}
public void Run()
{
Console.WriteLine($"{this.Name}가 뜁니다 (속도: {Speed})");
}
public void SpeedUp()
{
Speed += 10;
}
static public void Play(Cat catA, Cat catB)
{
Console.WriteLine($"{catA.Name}와 {catB.Name}가 함께 놉니다.");
}
public Cat clone()
{
return new Cat(this.Name, this.Color);
}
public void setAge(int Age)
{
this.Age = Age;
}
public int getAge()
{
return this.Age;
}
// 중첩 클래스
Home home;
public void MakeHome()
{
this.home = new Home();
}
public void Rest()
{
this.home?.Cure(this);
}
class Home
{
public void Cure(Cat cat)
{
cat.Age -= 1;
}
}
}
class KoreanCat: Cat
{
//public override void Sing() // Error: 오버라이딩 불가
//{
// Console.WriteLine("Cat Sing~");
//}
}
static class CatExtension
{
public static void Hug(this Cat cat)
{
Console.WriteLine($"{cat.Name}이 안아 줍니다.");
}
}
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Name = "키티";
cat.Meow();
// 참조형은 null 할당 가능
cat = null;
Cat cat1 = new Cat();
cat1.Meow();
Cat cat2 = new Cat("키티", "흰색");
cat2.Meow();
cat2.Hug();
cat2.Run();
cat2.SpeedUp();
Cat cat3 = new Cat("나비", "검정");
cat3.Run();
Cat.Play(cat1, cat2);
Cat cat4 = cat2; // 얕은 복사
Cat cat5 = cat2.clone(); // 깊은 복사
cat4.Meow();
cat5.Meow();
Cat cat6 = new Cat("키티", "흰색", 2);
cat6.setAge(3);
Console.WriteLine(cat6.getAge()); // cat6.Age 접근 불가
// 중첩 클래스
cat6.MakeHome();
Console.WriteLine($"나이: {cat6.getAge()}");
cat6.Rest();
Console.WriteLine($"나이: {cat6.getAge()}");
Animal animal = new Cat();
if (animal is Cat) { // is: 해당 형식에 해당하는지 여부 반환
Cat cat7 = animal as Cat; // as: 참조 형식에 대하여 형변환하며, 실패하는 경우 null 반환
cat7.Meow();
}
cat2.Sing(); // Cat Sing~
cat2.Sing2(); // Cat Sing2~
Animal animal1 = cat2;
animal1.Sing(); // Cat Sing~
animal1.Sing2(); // Sing2~
Console.ReadLine();
}
}
}