카테고리 없음

C# 문법 정리 5

빈코더 2022. 6. 16. 16:23
728x90

### Indexer

- Indexer는 클래스 객체의 데이타를 배열 형태로 인덱스를 써서 엑세스할 수 있게 해준다. 즉, 클래스 객체는 배열이 아님에도 불구하고, 마치 배열처럼 []를 사용하여 클래스 내의 특정 필드 데이타를 엑세스하는 것이다.

 

### 인덱서 정의

- C# Indexer는 특별한 문법인 this[ ] 를 써서 클래스 속성(Property)처럼 get과 set을 정의한다. 클래스 내부의 어떤 데이타를 리턴하는지는 클래스 디자인시 필요에 따라 정하게 될 것이고, 리턴 데이타 타입도 여러 가지로 지정할 수 있다.
- 입력 파라미터인 인덱스도 여러 데이타 타입을 쓸 수 있는데, 주로 int 나 string 타입을 사용하여 인덱스값을 주는 것이 일반적이다.

class MyClass
{
   private const int MAX = 10;
   private string name;

   // 내부의 정수 배열 데이타
   private int[] data = new int[MAX];

   // 인덱서 정의. int 파라미터 사용
   public int this[int index] 
   {
      get
      {            
         if (index < 0 || index >= MAX)
         {
            throw new IndexOutOfRangeException();
         }
         else
         {
            // 정수배열로부터 값 리턴
            return data[index];
         }
      }
      set
      {
         if (!(index < 0 || index >= MAX))
         {
            // 정수배열에 값 저장
            data[index] = value;
         }
      }
   }
}

class Program
{
   static void Main(string[] args)
   {
      MyClass cls = new MyClass();

      // 인덱서 set 사용
      cls[1] = 1024;

      // 인덱서 get 사용
      int i = cls[1];
   }
}

### 접근 제한자 (Access Modifier)

- 접근 제한자는 외부로부터 타입(클래스, 구조체, 인터페이스, 델리게이트 등) 혹은 그 타입 멤버들(메서드, 속성, 이벤트, 필드 등)로의 접근을 제한할 때 사용하는 것으로 다음과 같은 종류가 있다.

public 모든 외부(파생클래스 포함)에서 이 타입(Type: 클래스, 구조체, 인터페이스, 델리게이트 등)을 엑세스할 수 있다. (개별 타입 멤버의 엑세스 권한은 해당 멤버의 접근 제한자에 따라 별도로 제한될 수 있다)
internal 동일한 Assembly 내에 있는 다른 타입들이 엑세스 할 수 있다. 하지만, 다른 어셈블리에서는 접근이 불가하다.
protected 파생클래스에서 이 클래스 멤버를 엑세스할 수 있다.
private 동일 클래스/구조체 내의 멤버만 접근 가능하다.

- 접근 제한자는 public class A {} 와 같이 클래스, 구조체와 같은 Type 앞에 사용하거나 메서드, 속성, 필드 등의 클래스/구조체 멤버 앞에 사용하여 (예: protected int GetValue(); ) 접근을 제한하게 된다.

  • 클래스 멤버는 5가지의 접근 제한자를 (public, internal, private, protected, protected internal) 모두 가질 수 있지만, 구조체(struct) 멤버는 상속이 되지 않으므로 3가지의 접근 제한자만 (public, internal, private) 가질 수 있다.
  • 보통 클래스와 구조체는 네임스페이스 바로 밑에 선언하는데,이때 디폴트로 internal 접근 제한을 갖는다. 단, 클래스 내부에 Nested 클래스를 선언하는 것과 같이 Nested Type을 선언하면 디폴트로 private 접근 제한을 갖는다.
  • 인터페이스(interface)와 열거형(enum)의 멤버는 기본적으로 public 이며, 각 멤버에 별도의 접근 제한자를 사용하지 않는다.

### 파생클래스

- C#에서 부모 클래스인 기준 클래스(Base Class)로부터 상속하여 새로운 파생 클래스(Derived Class)를 만들 수 있다. 상속(inheritance)을 사용하게 되면 Base 클래스의 데이타 및 메서드들을 (public 혹은 protected 멤버의 경우) 파생클래스에서 사용할 수 있게 된다. 파생 클래스는 Base 클래스로부터 물려 받는 멤버들 외에 대개 자기 고유의 메서드와 데이타를 추가해서 사용하게 된다.

- C#에서 파생클래스를 정의하기 위해서는 클래스명 뒤에 Colon (:)을 찍고 Base 클래스명을 써 주면 된다. 제약점은 C#에서는 파생클래스가 단 하나의 Base 클래스로부터 상속되어져야 한다는 것이다. 즉, 하나의 파생클래스는 2개 이상의 Base 클래스를 가질 수 없다. 아래 예는 Base 클래스(Animal)로부터 Dog과 Bird라는 파생클래스들을 생성하는 예이다. 예제에서 각 파생클래스는 Base 클래스로부터 상속된 속성(Property)을 사용하고 있다.

// 베이스 클래스
public class Animal
{
   public string Name { get; set; }
   public int Age { get; set; }
}

// 파생클래스
public class Dog : Animal
{       
   public void HowOld() 
   {
      // 베이스 클래스의 Age 속성 사용
      Console.WriteLine("나이: {0}", this.Age);
   }
}

public class Bird : Animal
{       
   public void Fly()
   {
      Console.WriteLine("{0}가 날다", this.Name);
   }
}

### 추상 클래스 (Abstract Class)

C#의 클래스명 앞에 abstract라는 C# 키워드 붙이는 경우가 있다. 이를 추상 클래스(Abstract Class)라고 하는데, 이러한 추상클래스로부터는 객체를 직접 생성할 수 없다. 즉, new를 이용하여 클래스 객체를 생성할 수 없다.

또한 추상 클래스 안에 클래스의 임의의 멤버 앞에 abstract 키워드를 붙이는 경우가 있는데, 이는 해당 멤버가 구현되지 않았으며, 추상 클래스로부터 파생되는 파생클래스에서 반드시 그 멤버를 구현해 주어야 한다는 것을 의미한다. 파생 클래스에서 상속된 abstract 메서드를 구현하기 위해서는 override 라는 C# 키워드를 사용하여 그 메서드를 새로 정의하면 된다.

public abstract class PureBase
{
   // abstract C#키워드 
   public abstract int GetFirst();
   public abstract int GetNext();   
}

public class DerivedA : PureBase
{
   private int no = 1;

   // override C#키워드 
   public override int GetFirst()
   {
      return no;
   }

   public override int GetNext()
   {
      return ++no;
   }
}

### public / protected 멤버

- C#의 클래스 멤버 중 public으로 선언된 멤버들은 (파생클래스를 포함한) 모든 외부 클래스에서 엑세스 할 수 있다. 만약 다른 외부 클래스에서는 사용하지 못하게 하고 단지 파생클래스에서만 사용하도록 하고 싶다면, protected 라는 접근제한자(Access Modifier)를 사용한다.

 

### as 연산자와 is 연산자

- C#의 as 연산자는 객체를 지정된 클래스 타입으로 변환하는데 사용한다. 만약 변환이 성공하면 해당 클래스 타입으로 캐스팅하고, 변환이 실패하면 null 을 리턴한다.
- 이와는 대조적으로 캐스팅(Casting)을 사용하면, 변환이 실패했을 때 Exception을 발생시키게 되는데, 이를 catch하지 않으면 프로그램을 중지하게 된다.
- C#의 is 연산자는 is 앞에 있는 객체가 특정 클래스 타입이나 인터페이스를 갖고 있는지 확인하는데 사용한다.

class MyBase { }
class MyClass : MyBase { }

class Program
{
    static void Main(string[] args)
    {
        MyClass c = new MyClass();
        new Program().Test(c);
    }

    public void Test(object obj)
    {
        // as 연산자
        MyBase a = obj as MyBase; 

        // is 연산자
        bool ok = obj is MyBase; //true

        // Explicit Casting
        MyBase b = (MyBase) obj; 
    }
}

### static 메서드

- 정적(Static) 메서드는 인스턴스 메서드와는 달리 클래스로부터 객체를 생성하지 않고 직접 [클래스명.메서드명] 형식으로 호출하는 메서드이다. 이 메서드는 메서드 앞에 static 이라는 C# 키워드를 적어 주며, 메서드 내부에서 클래스의 인스턴스 객체 멤버를 참조해서는 안된다. 이 static 메서드는 인스턴스 객체로부터 호출될 수 없으며, 반드시 클래스명과 함께 사용된다.

public class MyClass
{
   private int val = 1;
   
   // 인스턴스 메서드
   public int InstRun()
   {
      return val;
   }
   
   // 정적(Static) 메서드
   public static int Run() 
   {
      return 1;
   }
}

public class Client
{
   public void Test()
   {
      // 인스턴스 메서드 호출
      MyClass myClass = new MyClass();
      int i = myClass.InstRun();

      // 정적 메서드 호출
      int j = MyClass.Run();
   }
}

### static 속성, 필드

- 정적(Static) 속성 및 필드는 위의 static 메서드와 같이 [클래스명.속성명]과 같이 사용하며, 다음 예와 같이 static을 앞에 붙여 정의한다. 클래스 내의 Non-static 필드들은 클래스 인스턴트를 생성할 때마다 메모리에 매번 새로 생성되게 되는 반면, static 필드는 프로그램 실행 후 해당 클래스가 처음으로 사용될 때 한번 초기화되어 계속 동일한 메모리를 사용하게 된다.

// static 필드
protected static int _id;

// static 속성
public static string Name { get; set; }

### static 클래스

- Static 클래스는 모든 클래스 멤버가 static 멤버로 되어 있으며, 클래스명 앞에 static 이라는 C# 키워드를 사용하여 정의한다. Static 클래스는 public 생성자(Constructor)를 가질 수 없지만 (왜냐 하면 static 클래스는 객체를 생성할 수 없으므로), static 생성자를 가질 수 있다. 이 static 생성자는 주로 static 필드들을 초기화 하는데 사용한다. 아래 예제는 static 클래스를 정의하고 사용하는 예이다.

// static 클래스 정의
public static class MyUtility
{
   private static int ver;

   // static 생성자
   static MyUtility()
   { 
      ver = 1;
   }

   public static string Convert(int i)
   {
      return i.ToString();
   }

   public static int ConvertBack(string s)
   {
      return int.Parse(s);
   }
}

// static 클래스 사용
static void Main(string[] args)
{
   string str = MyUtility.Convert(123);
   int i = MyUtility.ConvertBack(str);
}
728x90