클래스 문법
클래스
**클래스(Class)**에는 Field, Method, 생성자, 소멸자와 같은 *멤버(Member)*들을 포함할 수 있다. Filed는 클래스 내부에 선언된 속성으로 임의 형식의 변수를 말한다. 클래스의 멤버를 선언할 때 접근 한정자(private
, protected
, public
)를 생략하면 private
으로 정의된다. 객체를 생성할 때는 Reference Type으로 할당되며, new
키워드를 사용한다. C#에서는 delete
와 같은 키워드가 없어 의도적으로 할당 해제를 할 수 없다. 하지만 Garbage Collector에서 사용되고 있지 않은 객체를 할당 자동으로 해제하는 역할을 한다. 이때 소멸자가 호출되며 자동으로 해제되므로 그 시기를 알 수 없다. 클래스에서 this
키워드를 사용하여 자신의 객체를 참조할 수 있다.
class Person
{
private string name; // Field
public Person() // 기본 생성자(매개변수가 없는 생성자)
{
this.name = "이름 없음";
}
public Person(string name) // 매개변수가 있는 생성자
{
this.name = name;
}
~Person() // 소멸자
{
// 자원 해제
}
public void printPersonInfo() // Method
{
Console.WriteLine("Name : " + name);
}
}
클래스와 구조체 비교
특징 | 클래스 | 구조체 |
---|---|---|
형식 | Reference Type | Value Type |
할당 | Object obj = new Object(); |
Object obj; 또는 Object obj = new Object();
|
기본 생성자 정의 | 가능 | 불가능 |
상속 | 가능 | 불가능 |
인터페이스 | 가능 | 가능 |
abstract | 가능 | 가능 |
static | 가능 | 불가능 |
정적 멤버, 인스턴스 멤버
-
인스턴스 멤버 :
new
키워드를 사용해서 메모리에 할당된 객체의 멤버이다. -
정적 멤버 : 객체를 생성하지 않고도 사용할 수 있는 멤버로 정적 Filed, 정적 Method, 정적 생성자가 있다. 즉, 객체 개별로 사용하는 게 아니라 전역으로 사용할 수 있다. 멤버의 선언 앞에
static
을 사용하며, 객체가 처음 생성되거나 정적 멤버가 참조되기 전 자동으로 초기화 및 호출이 된다.
class Person
{
public static int id = 0; // 정적 Filed
public static int count; // 정적 Filed
static Person() // 정적 생성자
{
count = 0;
}
public static void printICount() // 정적 Method
{
Console.WriteLine("Count : " + count);
}
}
Console.WriteLine(Person.id); // 출력 결과 : 0
Person.printICount(); // 출력 결과 : 0
Namespace
Namespace를 정의함으로써 수많은 클래스와 Method를 분류하여 쉽게 구분할 수 있도록 해준다. .NET Framework에서는 Namespace를 사용하여 **기본 클래스 라이브러리(Basic CLass Library, BCL)**를 분류해 놓았다.
namespace InputDvice
{
class Keyboard { }
}
namespace OutputDvice
{
class Monitor { }
}
InputDvice.Keyboard keyboard = new InputDvice.Keyboard();
OutputDvice.Monitor monitor = new OutputDvice.Monitor();
-
using
키워드로 Namespace를 미리 선언하면 생략해서 사용할 수 있다.
using InputDvice;
using OutputDvice;
Keyboard keyboard = new Keyboard();
Monitor monitor = new Monitor();
인터페이스
**인터페이스(Interface)**는 세부적인 내용은 구현하지 않고, 객체가 기본적으로 가져야 할 공통적인 기능들만 정의한 것이다. 정의된 기능들은 객체에서 상속받으면 반드시 재정의해야 하고 그 안에서 세부적인 내용을 추가한다. 인터페이스의 멤버로는 Method, Property, Event, Indexer을 사용할 수 있다.
interface IKeyboard
{
void pressed();
}
interface IMouse
{
void click();
}
class Computer : IKeyboard, IMouse // 다중 상속 가능
{
public void pressed() { }
public void click() { }
}
인터페이스의 특징
- 기본적으로
public
과abstract
는 묵시적으로 적용된다. -
인터페이스를 상속받으면 자식 객체에서 구현할 때 반드시
public
을 사용해야 한다. - 접근 한정자(
private
,protected
,public
) 또는abstract
,virtual
,override
,new
등의 키워드를 사용할 수 없다. - 클래스는 다중 상속이 불가능하지만, 인터페이스는 다중 상속을 허용된다.
상속
**상속(Inheritance)**은 기존 클래스에서 이미 정의된 것들을 다른 클래스에서 사용하거나, 추가 또는 재정의할 수 있도록 한 것이다. 여기서 기존 클래스는 기반(Based) 클래스라고 하고, 상속받은 다른 클래스는 파생(Derived) 클래스라고 부른다. 파생 클래스는 하나의 기반 클래스만 상속 받을 수 있으며, 파생 클래스가 다른 클래스의 기반 클래스가 될 수도 있다. C#에서는 Base
키워드를 사용하여 기반 클래스의 생성자나 Method를 호출할 수 있다.
class BaseClass
{
private string a;
public BaseClass(string a)
{
this.a = a;
}
}
class DerivedClass : BaseClass
{
private int b;
public void DerivedClass(string a, int age) : base(a)
{
this.b = b;
}
}
다형성
**다형성(Polymorphism)**이란 객체를 여러 형태로 가질 수 있다는 객체지향 개념 중 하나이다. 클래스 간에 상속관계일 때 Method Override, Method Overload, 추상 클래스, 인터페이스와 같은 형태에서 다형성을 적용할 수 있다. C#에서는 Boxing, Unboxing, Delegate 등 다형성에 대한 다양한 기능들을 제공한다.
Method Override
Method Override는 기반 클래스에서 상속받은 Method를 파생 클래스에서 사용할 기능으로 재정의하는 것이다. 기반 클래스의 Method에 virtual
키워드를 사용하면 가상 Method로 정의된다. 파생 클래스에서 override
키워드를 사용하여 상속받은 Method를 재정의하며, 반드시 가상 Method와 같은 형태로 정의해야 한다.
class BaseClass
{
public virtual void printClassInfo() // 가상 Method
{
Console.WriteLine("Base Class");
}
}
class DerivedClass : BaseClass
{
public override void printClassInfo()
{
Console.WriteLine("Derived Class");
}
}
BaseClass b = new BaseClass();
BaseClass d = new DerivedClass();
b.printClassinfo(); // 출력 결과 : Base Class
d.printClassinfo(); // 출력 결과 : Derived Class
-
파생 클래스에서 재정의할 목적이 없을 때
override
를 사용하지 않으면 다음과 같은 warning이 나타난다. 이때 가상 Method를 재정의하지 않으려면new
키워드를 사용하면 된다.
현재 멤버가 해당 구현을 재정의하도록 하려면 override 키워드를 추가하십시오. 그렇지 않으면 new 키워드를 추가하십시오.
class DerivedClass : BaseClass
{
public new void printClassInfo()
{
Console.WriteLine("Derived Class");
}
}
-
Property로 정의된 Method도
override
키워드를 사용해 재정의할 수 있다.
class Item
{
private int _price;
public virtual int price
{
get { return _price; }
}
}
class UsedItem : Item
{
public override int price
{
get { return price * 0.5; }
}
}
- .Net Framework의 모든 클래스는 Object라는 기본 클래스를 상속받는다. Object에 기본적으로 포함된 Method도 Override를 할 수 있다.
class MyClass
{
public override string ToString() // Object.ToString() Override
{
return "My Class";
}
}
Method Overload
- Method Overload는 이름만 같은 Method가 매개변수의 개수와 형식만 다르게 다중 정의(Overloading)하는 것이다.
class Mathematics
{
public int Add(int value)
{
return value;
}
public int Add(int value1, int value2)
{
return value1 + value2;
}
public double Add(double value1, double value2)
{
return value1 + value2;
}
}
연산자 Overload
연산자 Overload는 정적 Method에 +
, -
, *
, /
등과 같은 연산자를 operator
키워드를 사용해 다중 정의(Overloading)하는 것이다.
class Adder
{
public int value;
public Adder(int value)
{
this.value = value;
}
public static Adder operator +(Adder op1, Adder op2)
{
return new Adder(op1.value + op2.value);
}
}
Adder number1 = new Adder(3);
Adder number2 = new Adder(2);
Adder result = number1 + number2;
Console.WriteLine(result.value); // 출력 결과 : 5
추상 클래스
추상 클래스는 기반 클래스에 추상 Method를 정의하여 Method Overload와 같은 역할을 할 수 있게 한다. Method Overload를 상속받은 Method는 override
와 new
키워드를 사용하여 재정의 여부를 정할 수 있지만, 추상 Method를 상속받은 Method는 반드시 재정의해야 하므로 override
키워드를 사용해야 한다. 추상 클래스를 정의할 때는 class
앞에 abstract
예약어를 사용하며, 추상 클래스는 객체로 선언해 사용할 수 없다. 또한, 추상 클래스의 모든 멤버가 추상 Method로 정의되어 있으면 인터페이스와 같은 역할을 한다.
abstract class AbstractClass
{
private string _name;
public string name
{
get { return _name; }
}
public abstract void printClassInfo(); // 추상 Method
}
class MyClass : AbstractClass
{
public virtual void printClassInfo()
{
Console.WriteLine("My Class");
}
}
인터페이스
(이미 현재 문서에 포함된 내용이다.)
Property
클래스에서 Field 값을 읽고 쓰기 위해 접근자 Method를 사용한다. 하지만 Field를 생성할 때마다 일일이 정의해야 하고, 코드가 길어져 가독성이 떨어진다. Property를 사용하면 이를 단순화 시킬 수 있다.
-
get
과set
접근자를 사용하여 Property를 사용할 수 있다.set
접근자에는 매개변수 대신에value
키워드를 사용한다.
class Person
{
private string _name;
public string name
{
get { return _name; }
set { _name; = value; }
}
}
Person person;
person.age = "김선욱";
Console.WriteLine(person.name); // 출력 결과 : 김선욱
- C# 3.0부터는 *자동구현 Property**를 제공하여 더 간결하게 정의할 수 있다.
class Person
{
public string name { name; private name; } // 접근 한정자 사용 가능
}
Partial
Partial Type
Partial Type는 클래스, 구조체 또는 인터페이스의 정의를 여러 개로 나누는 것이다. Partial Type을 정의할 때는 partial
키워드를 사용하며, 여러 파일로도 나누어 정의할 수도 있다.
// Project.model.cs
partial class Project
{
public void Model();
}
// Project.view.cs
partial class Project
{
public void View();
}
// Project.controller.cs
public partial class Project
{
public void Controller();
}
Partial Method
Partial Method는 Partial Type의 Method를 나누어 정의하는 것이다. Method를 선언과 본문으로 나누며 반드시 void
로 반환해야 한다. Partial Method은 암묵적으로 private
로 선언되므로 접근 한정자를 따로 사용할 수 없다.
partial class Project
{
partial void printProjectInfo(string name);
}
public partial class Project
{
partial void printProjectInfo(string name)
{
Console.WriteLine("Project : " + name);
}
}