10/10 캡슐화

정수현·2024년 10월 20일

C#

목록 보기
1/10
post-thumbnail

OOP

⑴ 캡슐화 ⑵ 인자 전달 ⑶ 상속 ⑷ 다형성

  • 이 글에서는 캡슐화와 인자 전달에 대해 정리하였다.

캡슐화

  • 여러 개의 멤버를 하나의 형식으로 묶어 하나의 형식으로 정의한다.
    ex) 구조체, 클래스 ..

정적 멤버, 비정적 멤버

  • static

접근 한정자

  • public : 어디서든 접근 가능
  • private : 같은 클래스 내에섬나 접근 가능
  • protected : 같은 클래스 및 파생 클래스에서만 접근 가능
  • internal : 동일한 어셈블리 내에서만 접근 가능
    밑에서 더 자세하게 다룸

목차

¹ 캡슐화
² 인자 전달
³ 생성자 & 소멸자
⁴ 객체 멤버와 정적 멤버
⁵ 접근 한정자



캡슐화

⑴ 멤버필드 ⑵ 멤버 속성 ⑶ 생성자, 소멸자 ⑷ 기능 메서드

캡슐화 대상

멤버 필드

  • 클래스나 구조체의 캡슐화되어 있는 일부 데이터
class Man
{
    private string name;
    private int hp = 0;
}
  • 접근한정자 priavte를 통해 외부에서 접근하지 못하도록 한다.

  • 멤버 속성이나 멤버 메서드를 통해 외부와 접근한다.

멤버 속성

  • 멤버 필드에 있는 값은 은닉되어 있으므로 속성을 통해 외부에 제공한다.
    접근 한정자 public을 사용한다.
  • 멤버 속성은 맨 글자를 대문자로 설정한다.
  • get블록 - 선언한 형식을 반환해야 한다.
  • set블록 - value이름으로 전달된 값을 사용한다.
public string Name
{
    get { return name; }
}
  
public int Hp
{
    get { return hp; }
    private set { hp = value; }
}
  • Name - get블록만 선택적으로 정의했다.
  • Hp - priavet set 클래서 내부에서만 접근할 수 있도록 설정했다.


인자 전달

함수의 매개 변수 전달 방식

입력 매개 변수를 전달하는 방법
① 값 형식 - 호출한 곳의 변수와 호출 받은 곳의 변수는 서로 독립적이다.
② 참조 형식 - 두 곳의 변수가 같은 개체를 참조한다. ref, out

  • ref 방식 - 메서드 내에서 값이 변경되면 호출한 곳에서도 알 필요가 있을 때 사용된다.
  • out 방식 - 전달되는 매개 변수는 반환 형식이 하나라는 한계를 극복하기 위해서 사용한다.

ref 방식 예시

static void Main(string[] args)
{
    int a=2, b=2, c=2;
    
    Foo(a, ref b, out c);
    
    Console.WriteLine("Main >> a: {0}, b: {1}, c: {2}");
}
private static void Foo(int a, ref int b, out int c)
{
    a=3;
    b=3;
    c=3;
    
    Console.WriteLine("Foo >> a: {0}, b: {1}, c: {2}");
}
  • 실행 결과
    Foo >> a: 3, b: 3, c: 3
    Main >> a: 2, b: 3, c: 3

out 방식 예시

static void Main(string [] args)
{
    int a=2, b=3;
    int sum, gap;
    
    AddAndGap(a, b, out c);
    
    Console.WriteLine("sum: {0}, gap: {1}", sum, gap);
}

private static int AddAndGap(int a, int b, out int c)
{
    gap = a-b;
    return a+b;
}
  • 실행 결과
    sum: 5, gap: -1

구조체와 클래스의 매개변수 전달 방식

구조체 - 값 형식
-> 변수는 변수 선언을 통해 메모리가 할당되므로 변수마다 독립적으로 할당된 메모리를 사용한다.
-> 단순히 값이 복사된다.

클래스 - 참조 형식
-> 변수 선언이 아닌 개체 생성을 통해 메모리가 할당된다.
-> 개체를 참조하는 변수를 다른 변수에 대입하면 두 개 의 변수는 같은 개체를 참조한다.
-> 두 변수 모두 값이 같이 변한다.

struct ExStruct
{
    public int Val { get; set; }
}

class ExClass
{
    public int Val { get; set; }
}

static void Main(string[] args)
{
    // 값 형식 - 구조체
    ExStruct es1 = new ExStruct();
     es1.Val = 1;
    ExStruct es2 = es1;
    es2.Val = 2;

    Console.WriteLine("es1.Val: {0}, es2.Val: {1}", es1.Val, es2.Val);

    // 참조 형식 - 클래스
    ExClass ec1 = new ExClass();
    ec1.Val = 5;
    ExClass ec2 = ec1;
    ec2.Val = 6;

    Console.WriteLine("ec1.Val: {0}, ec2.Val: {1}", ec1.Val, ec2.Val);
}
  • 실행 결과
    es1.Val: 1, es2.Val: 2
    ec1.Val: 6, ec2.Val: 6

값 형식과 참조형식을 값 방식으로 전달했을 때

  • 값 형식(구조체)
    -> 호출한 곳과 피 호출 메서드의 변수는 독립적이다
    -> 호출한 곳의 값을 복사하여 전달받기 때문에 피 호출 메서드의 변수 값을 변경하였을 때 호출한 곳의 변수는 영향 X

  • 참조 형식(클래스)
    -> 호출한 곳과 피 호출 메서드의 변수는 같은 개체를 참조한다.
    -> 정보가 변경된다.

private static void ExampleValueType(ExStruct es)
{
    es.Val = 5;
}

private static void ExampleRefernceType(ExClass ec)
{
    ec.Val = 5;
}

static void Main(string[] args)
{
    ExStruct es = new ExStruct();
    ExampleValueType(es);
    Console.WriteLine("es.Val {0}", es.Val);

    ExClass ec = new ExClass();
    ExampleRefernceType(ec);
    Console.WriteLine("ec.Val {0}", ec.Val);
}
  • 실행 결과
    es.Val: 0
    ec.Val: 5



생성자 & 소멸자

생성자

  • 정의한 클래스나 구조체의 개체가 생성될 때 수행할 코드를 작성하는 메서드
  • 반환 형식을 명시할 수 없다.
  • 형식 이름과 같은 이름을 갖는다.
  • 종류
    ⑴ 기본 생성자 (입력 매개 변수 X) - 클래스에서 사용한다.
    ⑵ 입력 매개 변수 O - 구조체에서 사용한다.
    ⑶ 정적 생성자 - 정적 멤버ㅓ ..?
  • 클래스나 구조체 내에 생성자를 정의하지 않으면, 암시적으로 기본 생성자가 만들어지며 멤버들을 기본값(0)으로 초기화한다.

생성자를 정의하지 않았을 때

class Example
{
    public int Val
    {
        get; set;
    }
}

class Program
{
    static void Main(string [] args)
    {
        Example ex = new Example();
        Console.WriteLine("ex.Val : {0}", ex.Val);
    }
}
  • 실행 결과
    ex.Val : 0

  • 클래스나 구조체 내에 생성자를 정의하지 않으면, 자동으로 기본 생성자가 만들어지며 멤버들의 값을 기본값 0로 초기화한다.

정적 생성자가 호출되는 시점

  • 정적 멤버가 있을 때 정적 멤버들의 값을 초기화한다.
class Example
{
    static Example
    {
        Console.WriteLine("정적 생성자 수행");
    }
    
    public static void Foo()
    {
        Console.WriteLine("정적 메서드 Foo 수행");
    }
}

class Program
{
    static void Main(string [] args)
    {
        Example.Foo();
        Example.Foo();
    }
}
  • 실행 결과
    정적 생성자 수행
    정적 메서드 Foo 수행
    정적 메서드 Foo 수행
  • 단 한번만 수행 되어야 하는 작업을 정의할 때 사용된다.
  • 직접 호출할 수 없으며, 접근 한정자를 명시할 수 없다.
  • 어떤 코드보다도 먼저 수행된다.

전용 생성자를 이용해 단일체를 구현

  • 전용 생성자 : 접근 지정이 private 로 되어 있는 것
  • 개체를 하나만 생성해야 할 때 사용한다.
  • Singleton 패턴이라고 부른다.
class Singleton
{
    static Singleton singleton;
    
    public static Singleton Instance
    {
        get { return singleton; }
    }
    
    static Singleton()
    {
        singleton = new Singleton();
    }
    
    private Singleton()
    {
    }
  • static Singleton singleton;
    : 단일체 정적 멤버

  • public static Singleton Instance
    : 단일체를 참조할 때 사용하는 정적 속성

  • static Singleton()
    : 정적 생성자
    singleton = new Singleton();
    : 단일체 생성

  • private Singleton
    : 단일체로 구현하기 위해 접근 지정을 private으로 설정


소멸자

  • 개체가 소멸할 때 수행해야 할 작업에 대한 코드
  • 클래스에서만 정의한다. (구조체에서 X)
  • 소멸자의 접근 한정자는 개발자가 명시하지 않는다.
  • 중복 정의할 수 없다.
class Man
{
    ~ Man()
    { }
}



상수와 읽기 전용

상수

  • const 키워드를 사용한다.
  • 상수 멤버 필드를 캡슐화하면 개체의 멤버가 아닌 정적 멤버가 된다.
  • 이럴 때 static 키워드를 같이 사용할 수 없다.
  • 고정된 값 (파이, 최대값 제한)으로 절대 변하지 않는 경우에 사용한다.
public calss Constants
{
    pubilc const int MaxValue = 100; //기본타입은 선언 시 반드시 초기화
    public const string Message = "안녕하세요";
    
    public const object Myobject = null; //참조 타입은 null로 초기화
    //public const List<int> Numbers = new List<int>(); //오류!!!!
}
  • 컴파일 시간에 상수값이 정해진다.
  • 선언 시 반드시 초기화해야 한다.
  • 기본 타입(int, double, string)만 사용 가능하다.
  • 참조 타입null로만 초기화할 수 있다.
  • 객체를 상수 멤버로 사용하려면 읽기 전용(readonly 필드)을 사용한다.

읽기 전용

  • readonly 키워드를 사용한다.
  • static 키워드를 명시해야 정적 멤버가 될 수 있다.
  • 읽기 전용은 생성자에서 초기화할 수 있다.
  • 동적으로 생성되는 객체나 런타임에 결정되는 값이 필요할 때 사용한다.

읽기 전용 멤버를 생성자에서 초기화

public class ReadOnlyExample
{
    public readonly List<int> Numbers; //멤버 필드
     
    public ReadOnlyExample()
    {
        Numbers = new List<int> { 1, 2, 3 }; //생성자에서 초기화
    }
     
    publlic void ModifyLList()
    {
        Numbers.Add(4); //참조는 변경 X, 내용은 변경 O
    } 
}
  • 런타임에 값을 정할 수 있다.
  • 필드를 선언 시생성자에서 초기화할 수 있다.
  • 참조 타입을 사용할 수 있다. (참조 자체 변경 X, 객체 내용 변경 O)



연산자 중복 정의

  • static 키워드를 사용해 정적 멤버로 만든다.
    public으로 접근을 한정시킨다.
    operator 키워드와 연산 기호를 명시한다. (메서드 이름 X)

연산자 중복 정의 예시

class Man
{ 
    string name;
    
    public Man(string name)
    { 
        this.name = name;
    }
    
    public static bool operator == (Man man, string name)
    {
        return man.name == name;
    }
    
    public static bool operator != (Man man, string name)
    {
        return !(man == name);
    }
}
  • 대응되는 연산자 중복 정의도 정의해야 한다.
    ex) == 연산자를 중복 정의하려면, != 연산자도 중복 정의해야 한다.

형 변환

  • static 키워드와 static 메서드를 사용해야 한다.
  • 동일한 유형으로 변환할 수 없다.
public static implicit operator string (Man man)
{    
    return man.name;
}
  • 묵시적 형 변환
    implicit 키워드 사용,
    변환하고자 하는 형식을 operator 뒤에 명시한다.

  • 명시적 형 변환
    explicit 키워드 사용



개체의 멤버와 정적 멤버

  • 정적 멤버
    static, const 키워드가 붙어 있는 멤버들
    ⑵ 클래스의 이름으로 접근이 가능하다.
    ⑶ 정적 멤버는 개체 멤버를 사용할 수 없다.

  • 개체 멤버
    ⑴ 그 외의 다른 멤버들
    ⑵ 개체 멤버는 변수로 접근한다.

  • 정적 클래스 static class Class { }
    ⑴ 클래스 키워드 앞에 static 키워드를 명시한 클래스
    ⑵ 정적 클래스의 모든 멤버는 정적 멤버이다.
    ⑶ 객체 생성이 불가능하다.
    static class MyClass { }
    MyClass myClass = new MyClass();
    //사용 불가

static class EHGlobal
{
    public const int max_man = 500;
    public static int GetNum()
    {
        int num = 0;
        try
        {
            num = int.Parse(Console.ReadLine());
        }
        catch {    }
        
        return num;
    }
}



접근 한정자

  • private 나만 접근 가능
    ⑴ 멤버에 접근 한정자가 명시되어 있지 않으면private으로 지정된다.
    ⑵ 해당 형식 내부에서만 접근할 수 있게 한다. (잘못된 사용을 차단)

  • protected 자식까지 접근 가능
    자식 클래스에서는 접근해도 되지만 다른 형식에서는 사용하면 안되는 멤버를 지정한다.

  • public 누구나 접근 가능 (모든 곳에서 접근이 가능하다.)

  • internal 형식 외부에서도 접근이 가능하다.

0개의 댓글