C# - C# 학습 정리 + Windows Form (3)

업데이트:

C# 프로그래밍

제네릭

기본

class Wanted<T>
{
    public T value;
    public Wanted(T value)
    {
        this.value = value;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Wanted<string> wantedString = new Wanted<string>("문자");
        Wanted<int> wantedInt = new Wanted<int>(3);
    }
}

두 개 이상의 제네릭을 사용할 떄에는 쉼표로 구분합니다.

class Test<T, U>
{

}


where 키워드

where 키워드를 사용하면 제네릭 자료형을 제한할 수 있습니다.

class Test<T, U>
{
    where T: class
    where U: struct
}

위의 코드는 T는 클래스를 허용하고 U는 구조체만을 허용한다는 의미입니다.


class Test<T, U>
{
    where T: IComparable
    where U: IComparable, IDisposable
}

위의 코드는 IComparable이거나 이를 상속받은 것이어야 하는 의미입니다.


인덱서

리스트나 배열을 사용할 때, [] 형태로 인덱스를 많이 사용했습니다.
인덱서를 이용하면 클래스에서 []를 사용하여 배열처럼 사용할 수도 있습니다.

  class Wanted
    {
        public int this[int i] { get { return i * i; } }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Wanted square = new Wanted();
            Console.WriteLine(square[10]);
            Console.WriteLine(square[12]);
        }
    }


out 키워드

out 키워드를 사용하면 값을 여러 개 반환할 수 있습니다.

예를들어 TryParse() 메서드가 out 키워드를 사용하는 대표적인 메서드입니다.

public static bool TryParse(string s, out int result)

여기서 변경가능한 문자열을 넣으면 true를 반환하고 그게 아니라면 false를 반환합니다.

int output;
bool result = int.TryParse(Console.ReadLine(), out output);

if (result)
{
    Console.WriteLine(output);
}
else
{
    Console.WriteLine("실패");
}

실제로 메서드를 사용할 땐 다음과 같이 사용합니다.

static void NextPosition(int x, int vx, out int rx)
{
    rx = x + vx;
}

static void main(string[] args)
{
    int x = 0;
    int vx = 1;
    NextPosition(x, vx, out x);
    Consol.WriteLine("다음좌표: " + x); // 1
}


구조체

구조체는 간단한 객체를 만들 때에 사용하는 형식입니다.
클래스와 거의 동일한 구문을 사용하지만 복사 형식이 다르고 제한이 많습니다.
상속이 불가능하고 인터페이스를 구현할 수도 없지만 클래스보다 안정성이 높습니다.

struct Point
{
    public int x;
    public int y;
}

static void Main(string[] args)
{
    Point point;
    point.x = 10;
    point.y = 10;
}

구조체는 다음과 같이 생성자를 선언할 수 없습니다. 자동으로 정의되기 때문입니다.
그러나 매개변수가 있는 생성자는 생성할 수 있습니다. 다만 반드시 그 생성자 내에서 필드 변수 전부를 초기화하여야합니다. 또한 구조체의 필드 변수는 선언과 동시에 초기화를 할 수 없습니다.

struct Point
{
    public int x;
    public int y;

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

static void Main(string[] args)
{
    Point point;
    point.x = 10;
    point.y = 10;
}

이처럼 구조체의 가장 중요한 의미는 안정성 이기 때문에 제약이 많습니다.
객체를 멤버 변수로 사용하더라도 null로 생성자에서 초기화를 시켜 주어야합니다.


또한 구조체는 클래스 객체와 달리 값복사가 일어납니다.

// 클래스 참조 복사
PointClass pointClassA = new PointClass(10,20);
PointClass pointClassB = pointClassA; 

// 구조체 값 복사
PointStruct pointStructA = new PointStruct(10,20);
PointStruct pointStructB = pointStructA;


인터페이스

IComparable

Icomparable 인터페이스는 비교가 가능한 자료에 적용하며 모델 클래스에 많이 적용합니다.

class Product
        {
            public string Name { get; set; }
            public int Price { get; set; }

            public override string ToString() 
            {
                return Name + " : " + Price;
            }
        }
        static void Main(string[] args)
        {
            List<Product> list = new List<Product>()
            {
                new Product { Name = "고구마", Price = 1500 },
                new Product { Name = "감자", Price = 2400 }
            };
            list.Sort();

            foreach(var item in list)
            {
                Console.WriteLine(item);
            }
        }

위의 코드에서 정렬을 하게되면 기준이 없기 떄문에 예외가 발생합니다.

따라서 비교하는 기준을 정의해야하는데 여기서 IComparable 인터페이스를 상속하여 CompareTo 메서드를 구현합니다.

ToString()을 재정의하는 이유

모든 객체는 문자열 표현을 반환할 때 ToString()이 호출됩니다.
따라서 특정 클래스에서 이를 재정의하면 Console.WriteLine() 호출 시 재정의된 값으로 반환한다.

class Product: IComparable
        {
            public string Name { get; set; }
            public int Price { get; set; }

            public int CompareTo(object obj)
            {
                return this.Price.CompareTo((obj as Product).Price);
            }

            public override string ToString()
            {
                return Name + " : " + Price;
            }
        }
        static void Main(string[] args)
        {
            List<Product> list = new List<Product>()
            {
                new Product { Name = "고구마", Price = 1500 },
                new Product { Name = "감자", Price = 2400 }
            };
            list.Sort();

            foreach(var item in list)
            {
                Console.WriteLine(item);
            }
        }


IDisposable

IDisposable 인터페이스는 using 블록을 사용할 때 자동으로 호출되는 규약입니다.

class Program
{
    class Dummy: IDisposable
    {
        public void Dispose()
        {
            
        }
    }

    static void Main(string[] args)
    {
        using (Dummy dummy = new Dummy())
        {

        }
    }
}

위의 코드에서 using 블록이 끝날 때 Dispose 메서드가 호출됩니다.


인터페이스 생성

인터페이스는 여러 곳에서 사용할 수 있으므로 보통 별도의 파일에 생성합니다.

namespace InterfaceBasic
{
    interface IBasic
    {

    }
}


인터페이스 사용

인터페이스는 다른 언어와 마찬가지로 내부 구현을 할 수 없습니다.

namespace InterfaceBasic
{
    interface IBasic
    {
        int TestMethod();
        int TestProperty { get; set; }
    }
}
class TestClass : IBasic
    {
        public int TestProperty { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public int TestMethod()
        {
            throw new NotImplementedException();
        }
    }

인터페이스 클래스가 부모이기 때문에 다형성을 구현할 수 있습니다. 그렇게 되면 다중 상속을 구현할 수 있게 됩니다.


인터페이스 다중 상속

대부분의 형대 언어는 다중 상속을 허용하지않지만 인터페이스는 다중 상속이 허용됩니다.

class TestClass : IBasic, IComparable
    {
        public int TestProperty { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public int CompareTo(object obj)
        {
            throw new NotImplementedException();
        }

        public int TestMethod()
        {
            throw new NotImplementedException();
        }
    }


주의 사항

여러 개의 인터페이스를 상속할 때, 메서드 이름이 겹칠 수 있는데 그 때에는 인터페이스이름.메서드이름() 형태로 구분해서 구현합니다.


윈도우 폼

메뉴

도구상자 - MenuStrip


상태 표시줄

도구상자 - StatusStrip


  • 참고자료: C# 프로그래밍 - 한빛아카데미

댓글남기기