클래스


클래스는 속성(변수)와 기능(메소드)를 가지고있는 설계도이며, 객체는 실제 데이터를 가질수 있는 실체(instance)이다.

// int a=20;에서도 int는 32비트 정수를 담는 객채를 위한 설계도 (클래스) 이고 , a 는 실제 데이터를 담을수 있는 객체이다.


클래스의 선언


class 클래스이름

{

//데이터와 메소드

}


class Cat

{

public string Name;

public string Color;

public void Meow()

{

Console.WriteLine("{0} : 야옹",Name);

}

}

Name과 Color같이 클래스 안에 선언된변수를 필드라고하며, 필드,메소드,이벤트,프로퍼티등 클래스내에 선언되어있는 요소들을 일컬어 멤버라고한다


객체의 생성


클래스_이름 객체_이름 = New 생성자;

// 생성자는 클래스_이름() 이며, 새로운 객체를 생성하는 역할을 한다. new는 생성자와함께쓰여 객체를 생성하는 연산자이다


Cat kitty =new Cat();

kitty.Color = "하얀색";

kitty.Name = "키티";

kitty.Meow();


모든 클래스는 복합데이터형식이며, 참조형식이다. 그리고 위의 예제에서 Kitty의값은 null이다. kitty는 값을 참조할뿐 값을 가지지 않기때문.


생성자와 소멸자


생성자 - 생성자의 이름은 클래스와 이름이 같다 , 반환형식이 없다. 

생성자를 만드는 이유 : 객체를 생성할 시점에 객체의 필드를 원하는 값으로 초기화 하기 위해


생성자의 선언


Class Cat() // 클래스의 선언

{

public Cat() // 생성자의 선언

{

Name = ""; // 초기화

Color = "";

}

public Cat( string _Name , string _Color );

{   

Name = _Name;

Color = _Color;

} // 생성자 메소드 또한 오버로딩 가능

public string Name;

public string Color;


}


소멸자 - 소멸자의 이름은 클래스의 이름 앞에 ~ 를 붙인것이다.

소멸자는 매개변수도없고 한정자도 사용하지않으며, 오버로딩도 불가하고, 직접호출도 불가능하다.(CLR의 가비지컬렉터가 객체가 소멸되는 시점을 판단하여 알아서 소멸자를 호출함)


소멸자의 선언


Class Cat() // 클래스의 선언

{

~Cat() // 소멸자의 선언

{

Console.WriteLine("{0} : 잘가" , Name);

}


}


가비지컬렉터가 언제 소멸자를 호출할지는 알 수 없기 때문에 소멸자는 되도록 구현하지 않는것이 성능상 좋다.


정적 필드와 메소드


정적(static)은 메소드나 필드가 인스턴스가아닌 클래스 자체에 소속되도록 지정하는 한정자이다 .

어떤 필드나 메소드가 클래스에 소속된다는것은 그 필드가 프로그램 전체에서 유일하게 존재한다는것을 뜻한다.

인스턴스에 소속된 필드의 경우 객체 이름이 obj , 필드가 a 라고 했을때, 객체안의 값에 접근할때 obj.a =20; 이런식으로 인스턴스에서 접근한다.

하지만, 클래스에 속한 필드의 경우 그 존재가 유일하기때문에, 클래스 이름이 Myclass , 필드가 a 라고 할때 Myclass.a =20; 이런식으로 클래스 자체에서 직접 접근한다. 프로그램전체에 걸쳐 공유해야하는 변수는 정적필드로 만들 필요가 있다. 


정적메소드


class MyClass

{

public static void StaticMethod()

{

//

}

public void InstanceMethod()

{

      //

}

}

MyClass.StaticMethod(); // 인스턴스를 만들지 않고 호출 가능

Myclass obj = new Myclass();

obj.InstanceMethod();

정적 메소드는 인스턴스를 생성하지 않아도 사용할 수 있다. 인스턴스 메소드는 인스턴스를 생성한 후 메소드를 사용해야한다.

보통 객체 내부데이터를 이용할경우 인스턴스 메소드를 이용하고, 이용하지 않을경우 정적메소드를 이용한다.


객체 복사하기 - 얇은복사와 깊은 복사


얇은 복사


class MyClass

{

public int MyField1;

public int MyField2;

}

class MainApp

{

static void Main(string[] args)

{

MyClass source = new MyClass();

source.MyField1 = 10;

source.MyField2 = 20;

MyClass target = source;

target.MyField2 = 30;

}

}


클래스는 참조형식이기 때문에, 객체 이름에는 참조값이 저장되어 있다. 따라서 위의 경우처럼 얕은 복사를 할 경우에는, 참조값이 복사되어 같은 주소에 저장되어 있는 변수를 가리키게 된다. 이경우, source 와 target의 값은 둘다 10 , 30으로 바뀐다.


깊은복사


C#에서는 깊은복사를 지원하지 않는다. 따라서  깊은복사를 하고싶을땐 ( 객체내의 변수를 직접복사 ) 코드를 만들어야한다.


class MyClass

{

public int MyField1;

public int MyField2;


public Myclass DeepCopy()

{

MyClass NewCopy = new MyClass();

NewCopy.MyField1 = this.MyField1; // this는 자기 자신 클래스를 나타낸다.

NewCopy.MyField2 = this.MyField2;

return NewCopy;  

}

}


ICloneable


class Myclass : ICloneable

{

public int MyField1;

public int MyField2;


public object Clone()

{   

MyClass NewCopy = new MyClass();

NewCopy.MyField1 = this.MyField1; // this는 객체가 자기 자신을 나타낼때 사용하는 키워드이다.

NewCopy.MyField2 = this.MyField2;

return NewCopy;  

}

}


ICloneable은 깊은 복사 기능을 가질 클래스 .NET의 다른 유틸리티 클래스나 다른 프로그래머가 만든 코드와 호환되도록 하고싶다면 사용한다. ICloneable인터페이스는 Clone() 메소드만을 가진다


this키워드


this는 객체가 자기 자신을 나타낼 때 사용하는 키워드다. 


class Employee

{

private string Name;


public void SetName(Name)

{

this.Name = Name; // 매개변수와 자기 자신 내부에있는 필드가 이름이 같을때 , 그 모호성을 this 키워드를 이용해 풀 수 있다. 

}

}


this() 생성자

class Myclass
{
int a, b ,c;

public MyClass()
{
this.a = 5425;
}
public MyClass(int b)
{
this.a = 5425;
this.b = b;
}
public MyClass(int b, int c)
{
this.a = 5425;
this.b = b;
this.c = c;
}
}

이 코드를 this() 를 이용하면 () 안에 매개변수와 같은 변수이름을 넣어 그 생성자를 호출할 수 있다.

class Myclass
{
int a, b ,c;

public MyClass()
{
this.a = 5425;
}
public MyClass(int b):this()
}
this.b = b;
}
public MyClass(int b , int c):this(b)
{
this.c = c;
}
}

this() 는 자기 자신의 생성자를가리킨다. 생성자에서만 사용될 수 있고 , 코드블록 앞쪽에서만 사용이 가능하다. //생성자의 오버로딩시 주로 사용된다.

접근한정자

코드를 짤때, 필요한 최소의 기능만을 노출하고, 내부를 감출것을 요구하는데, 그것을 은닉성이라고한다. 특히, 필드는 상수를 제외하고는 무조건 감추는것이 좋다. 이는 접근 한정자를 사용하여 감출수있다. private등으로 감추어진 필드는, 접근시 같은 클래스 내부에 선언된 public 메소드를 통해 접근해야한다.

public 클래스의 내부/외부 모든곳에서 접근 가능
protected  클래스 외부에서는 접근할 수 없지만, 파생 클래스에서는 가능 
private 클래스의 내부에서만 접근 가능. 파생에서도 불가능
internal 같은 어셈블리에 있는 코드만 public으로 접근 가능. 다른 어셈블리에 있는 코드에서는 private와 같은 수준의 접근성
protected internal 같은 어셈블리에 있는 코드에 대해서만 protected로 접근 가능. 다른 어셈블리에 있는 코드에서는 private와 같은 수준의 접근성
// 변수 선언시 접근한정자를 설정하지 않을 시 private로 자동 설정된다.
// 어셈블리란 DLL 혹은 EXE의 상태로 존재하는 코드들의 논리 묶음이다

클래스의 상속

객체지향 프로그래밍에서는 상속받는 클래스가 상속해줄 클래스를 지정한다. 클래스의 지정은 콜론(:) 을 붙이고 뒤에 기반클래스의 이름을 붙이면된다

class 기반클래스_이름
{   
//멤버
}

class 파생클래스_이름 : 기반클래스_이름
{
// 파생클래스에 선언된 멤버 + 기반클래스한테서 상속된 멤버( 아무 멤버를 선언하지 않아도 기반 클래스의 모든것을 물려받게됨)
// 단, private 로 선언된 멤버는 제외됨
}

파생클래스는 객체를 생성할 때 내부적으로 기반 클래스의 생성자를 호출한 후에 자신의 생성자를 호출하고, 객체가 소멸될 때는 반대로 파생클래스 소멸자가 호출하고 기반클래스의 소멸자를 호출한다.

base 키워드

this 키워드가 자기 자신을 가리키는 것과 마찬가지로 base 키워드는 기반클래스를 가리키는 키워드이다. base 키워드를 통해 기반 클래스의 멤버에 접근 수 있다.

base() 

this()가 자기 자신의 생성자인 것 처럼 base()는 기반클래스의 생성자이다. base( 매개변수 ) 이처럼 base()에 매개변수를 넘겨 호출하면, 기반클래스의 생성자에 매개변수를 전달할 수 있다.

class Base
{
protected string Name;
public Base(string Name)
{
this.Name = Name;
}
}

class Derived : Base // Base 기반함수를 상속받음
{
public Derived(string Name) : base(Name) // 기반클래스의 생성자에 Name매개변수를 전달하며 호출
{
Console.WriteLine ("{0}.Derived",this.Name);
}
}

sealed 한정자

sealed 한정자로 클래스를 수식하면 이 클래스는 상속봉인이 되어 이로부터 상속받으려는 시도를 컴파일러가 발견했을때, 에러메세지를 출력한다.

기반클래스와 파생클래스 사이의 형식변환

class Mammal
{
public void Nurse() { // }
}
class Dog : Mammal
{
public void Bark() { // }
}
class Cat : Mammal
{
public void Meow() { // }
}

Mammal mammal = new Mammal();
Dog dog = new Dog();
Cat cat = new Cat();
mammal  = new Dog(); // Mammal의 참조형식을 가지고있는 객체 mammal이 참조하는 메모리에 Dog형태의 인스턴스를 할당.
Dog dog = (Dog)mammal // 파생클래스에서 클래스 복사를 할때, 형식이 맞아야 하므로 기반클래스를 파생클래스의 형식으로 강제 형변환시킴
* 기반클래스 참조형식을 가지고있는 객체에 파생클래스의 인스턴스로 선언하는 것은 가능하지만,  파생클래스 참조형식을 가지고 있는 객체에 기반클래스의 인스턴스로 선언하는것은 불가능하다. 
ex) Mammal mammal = new dog; 가능 Dog dog = new Mammal(); 불가능

형변환을 위한 연산자 is 와 as

is 객체가 할당된 메모리의 인스턴스의 형식이 해당 형식에 해당하는지 검사하여 그 결과를 bool 값으로 반환
as 형식 변환 연산자와 같은 역할. /* (Dog)mammal */ 형변환 연산자는 변환에 실패할 경우 예외를 던지는 반면, as 연산자는 객체참조를 null로 만든다

Mammal mammal = new Dog(); 
Dog dog; // new 연산자를통해 메모리에  할당하지 않고 참조만 했음.
if (mammal is Dog)
{
dog = (Dog)mammal;
dog.Bark();
}

Mammal mammal2 = new Cat();
Cat cat = mammal2 as Cat
if (cat != null) // as의 형변환이 실패했을때, cat(객체참조)을 null 로 만듦
{
cat.Meow();
}

mammal은Mammal 타입의 참조형식을 가진 객체이기때문에 mammal.Bark();는 실패함. 하지만, mammal 은 Dog의 인스턴스를 가지고 있기때문에, (Dog)mammal 식의 형변환이 가능함. Dog dog = (Dog)mammal 일때, dog 에는 mammal 에 저장된 주소값과 같은 주소값이 복사되며, 같은곳을 가리키게 됨. 그리고 dog는 참조형식이  Dog 타입의 객체이기때문에, dog.bark()는 가능함. 
new Dog() 코드는 메모리에 Dog클래스의 인스턴스를 할당하는 역할,  Mammal mammal 은 메모리에 mammal의 참조 형식을 Mammal로 설정하는 역할이다(컴파일러는 mammal을 Mammal 형식의 객체로 인식).
일반적으로 형변환 연산자보다는 as 연산자를 사용하는쪽이 권장됨. 하지만 as 는 참조형식에 대해서만 가능하므로 값 형식의 객체(int a =2; double i = (double)a; 등) 은 형 변환 연산자를 사용해야한다
// 형변환 연산자, as 둘다 참조형식의 형태를 변환하는 것이다.

오버라이딩과 다형성

다형성은 한 객체가 여러개의 형태를 가질 수 있음을 의미한다 ( 하위 형식 다형성의 준말이다). 기반클래스는 자신으로부터 상속받아 만들어진 파생클래스를 통해 다형성을 가질 수 있다. 
파생클래스는 오버라이딩을 통해 기반클래스의 메소드를 재정의 할 수 있다. - 이때, 반드시 기반클래스의 메소드는 virtual 키워드로 한정되어있어야 한다.
또한, 만약 기반클래스에서 메소드가 private로 선언되었다면, 오버라이딩 할 수 없다.( 접근도 불가능하다)

using System;
namespace Overriding
{
class ArmorSuite
{
public virtual void Initialize()
{
Console.WriteLine ( " Armored" );
}
}

class IronMan : ArmorSuite
{
public override void Initialize()
{
base.Initialize();
Console.WriteLine("Repulsor Rays Armed");
}
}
class WarMachine : ArmorSuite
{
public override initialize()
{
base.Initialize();
Console.WriteLine("Double Barrel Cannons Armed");
Console.WriteLine("Micro-Rocket Launcher Armed");
}
}
class MainApp
{
static void Main(string[] args)
{
ArmorSuite armorSuite = new ArmorSuite();
armorsuite.Initialize(); 
ArmorSuite ironman  = new IronMan();
ironman.Initialize(); 
ArmorSuite warmachine = new WarMachine();
warmachine.Initialize();
}
}
}

메소드 숨기기
메소드 숨기기는 CLR에게 기반 클래스에서 구현된 버전의 메소드를 감추고 파생클래스에서 구현된 버전만을 보여주는것을 말한다.
오버라이딩과 다르게 , 기반클래스에서 virtual 키워드를 사용하지않고, 파생클래스의 신 버전의 메소드 반환형 앞에 new 키워드를 붙여준다.
오버라이딩과는 다르게, 메소드를 감출뿐, 사라지는것이 아니기때문에 기반클래스의 참조형식으로 생성된 객체에서 메소드를 실행하면, 기반클래스의 메소드가 그대로 실행된다.

class Base
{
public void MyMethod()
{
Console.WriteLine("Base.MyMethod()!!");
}
}

class Derived : Base
{
publicn new void MyMethod()
{
Console.WriteLine("Derived.MyMethod()!!");
}
}

Derived derived = new Derived();
derived.MyMethod(); // Derived.MyMethod()!! 출력
Base baseOrDerived = new Derived();
baseOrDerived.MyMethod(); // Base.MyMethod()!! 출력

오버라이딩 봉인하기
오버라이딩 봉인은 한번 오버라이딩 된 메소드에서만 봉인 가능하다.( 아예 오버라이딩을 하지 않을거라면 virtual 키워드를 쓰지 않으면 되기 때문)
사용방법은 오버라이딩할 메소드의 override 키워드 앞에 sealed 키워드를 붙여주면 된다.

class Base
{
public virtual void SealMe()
{
//
}
}

class Derived : Base
{
public sealed override void SealMe() // 선언시 SealMe() 메소드를 더이상 오버라이딩 할 수 없다.
{
//   
}
}

중첩클래스
클래스안에 소속된 클래스를 중첩클래스라고한다. 중첩클래스는 자신이 속한 클래스의 멤버에 자유롭게 접근할 수 있다 (private도 가능)
중첩클래스는 클래스 외부에 공개하고싶지 않은 형식을 만들고자 할때, 현재의 클래스의 일부분처럼 표현할 수 잇는 클래스를 만들고자 할때 사용한다.

class OuterMember
{
private int OuterMember;

private class NastedClass // private로 클래스를 선언하면 OuterMember외부에서는 노출되지않는다.
{
public void DoSomething()
{
OuterClass outer= new OuterClass();
outerclass.OuterMember =10;
}
}

분할 클래스

여러번에 나누어서 구현하는 클래스이다. 클래스의 구현이 길어질 경우, 여러파일에 나누어서 구현할 수 있게함으로써 소스관리의 편의를 제공한다.
분할클래스는 partial 키워드를 class 앞에 붙여서 선언한다.

partial class MyClass
{
public void Method1() {}
public void Method2() {}
}

partial class MyClass
{
public void Method3() {}
public void Method4() {}
}
MyClass obj = new MyClass();
obj.Method1();
obj.Method2();
obj.Method3();
obj.Method4();

확장 메소드
확장메소드는 기존클래스의 기능을 확장하는 기법이다. ex) string 클래스에 문자열을 뒤집는 기능을넣거나 int 형식에 제곱연산기능을 넣을 수 있다
using 네임스페이스로 확장메소드를 담는 클래스를 사용하면, 확장메소드를 기존클래스의 메소드처럼 사용 가능하다. ex) int a=2; a.Power(3); // Power은 참조형식이 int인 객체 a안에 선언된 메소드이다.

확장메소드의 선언

namespace 네임스페이스이름
{
public static class 클래스이름
{
pubilc static  반환형식 메소드이름( this 대상형식 식별자, 매개변수목록)
{
//
}
}
}

namespace MyExtension
{
public static class IntegerExtension
{
public static int Power (this int myint, int exponent) 
{
int result = myint;
for(int i =1; i<exponent; i++)
result = result*myint;
return result;
}
}
}

using MyExtension
...
int a = 2;
Console.WriteLine ( a.Power(3));

구조체

구조체의 선언

struct 구조체 
{
//필드
//메소드
}
// 클래스는 실세계의 객체를 추상화하려는데 그 의의가 있고, 구조체는 데이터를 담기위한 자료구조로 이용된다. 따라서 클래스는 private로만들어서 최대한 은닉성을 지켜야하는 반면, 구조체는 편의를 위해 필드와 메소드를 public 으로 선언하는일이 많다

클래스 - class키워드 , 참조형식, 얕은복사, new연산자와 생성자 필요, 매개변수없는 생성자 선언 가능, 상속 가능
구조체 - struct 키워드, 값형식 , 깊은복사 , 선언만으로 생성, 매개변수없는 생성자 선언 불가능, 모든 구조체는 System.Object 형식을 상속하는 System.ValueType으로부터 직접 상속받음.
// 구조체는 인스턴스가 선언된 블록이 끝나는 시점에서 메모리에서 사라짐.

struct MyStruct
{
public int MyField1;
public int MyField2;
public void MyMethod()
{
//
}
}

Mystruct s; // 생성자, new 키워드 없이 생성
s.MyField1 = 1; 
s.Myfield2 = 2;

Mystruct t;
t = s;
s.MyField1=3;
// t는 1, 2 값을 s는 3, 2 값을 갖게된다 (값형식)



+ Recent posts