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

업데이트:

C# 프로그래밍

Linq

Linq란?

Linq를 사용하면 컬렉션 형태의 데이터를 쉽게 다룰 수 있습니다.

SQL을 본따 만든 구문이 Linq입니다. 이를통해 C# 객체의 집합이나 서버의 데이터베이스 관리를 간단하게 관리할 수 있습니다.

  • 기존의 코드
List<int> input = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };
List<int> output = new List<int>();

// 기존 코드
foreach (var item in input)
{
    if (item % 2 == 0)
    {
        output.Add(item);
    }
}

return output;


  • Linq 적용 코드
List<int> input = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };

return from item in input 
    where item % 2 == 0 
    select item;
List<int> input = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };

var output = from item in input
            where item % 2 == 0 
            orderby item
            select item;

위의 예 처럼 from, in, where, orderby, select를 사용할 수 있습니다.
이외에도 SQL 쿼리문의 대부분을 사용할 수 있습니다.


from in Select 구문

from 에서 컬렉션의 아이템을들 지정합니다.

이후 select에서 원하는 형태로 값을 반환하여 결과를 만들 수 있습니다.

var output = from item in input
            select item * item;


where 구문

where 구문은 조건을 지정할 수 있습니다.

var output = from item in input
            where (item>5) && (item%2 == 0)
            select item * item;


orderby 구문

orderby 구문은 정렬해줍니다.

디폴트 값은 오름차순으로 지정됩니다.

  • ascending: 오름차순
  • descending: 내림차순
var output = from item in input
            where (item>5) && (item%2 == 0)
            orderby item descending
            select item * item;


꼭 var 키워드로 반환 변수를 정해야하는가?

var 키워드는 IEnumerable 인터페이스의 다형성을 이용해서 다양한 자료형을 지정하고 넣을 수 있습니다.

질의와 select 구문에 입력하는 자료형에 따라 굉장히 다양하게 바뀔 수 있기 때문에 가장 안정적인 var을 사용하는게 좋습니다.


다만 메서드에서의 경우는 다릅니다.

메서드 안에서 값을 var 키워드를 지정하여 반환할 수 없습니다.
메서드에서 반환을 할 때에는 다음과 같이 반환형을 정해주고 쿼리문도 반환형을 명시적으로 정해줍니다.

public List<int> SelectEven(List<int> input)
{
    return (from item in input
            where item % 2 == 0
            select item).ToList<int>();
}


익명 객체

C#은 다음과 같은 형식으로 익명의 객체를 생성할 수 있습니다.

여기서 A, B, C는 속성입니다.

new { <속성A> = <값>, <속성B> = <값> };
var output = from item in input 
            where item % 2 == 0 
            select new
            {
                A = item * 2,
                B = item * item,
                C = 100
            };

foreach (var item in output)
{
    Console.WriteLine(item.A);
    Console.WriteLine(item.B);
    Console.WriteLine(item.C);
}


XML 파싱

웹에서 XML 가져오기

static void Main(string[] args)
{
    string url = "https://xxxxx/xxx/";
    XElement xElement = XElement.Load(url);
    Console.WriteLine(xElement);
}


이리허게 출력하면 XML 데이터 전체가 출력됩니다.

파싱한 데이터에서 원하는 형태의 데이터만 출력하여 저장할 수 있습니다.
Descendants() 메서드를 활용합니다.

static void Main(string[] args)
{
    string url = "https://xxxxx/xxx/";
    XElement xElement = XElement.Load(url);

    // data 태그를 모두 선택
    var xmlQuery = from item in xElement.Descendants("data")
                select item;
    
    foreach (var item in xmlQuery)
    {
        Console.WriteLine(item);
    }
}


이제 다시 내부에서 필요한 태그를 선택하고 추출합니다.

static void Main(string[] args)
{
    string url = "https://xxxxx/xxx/";
    XElement xElement = XElement.Load(url);

    // data 태그를 모두 선택
    var xmlQuery = from item in xElement.Descendants("data")
                select item;
    
    foreach (var item in xmlQuery)
    {
        Console.WriteLine(item.Element("hour").Value);
        Console.WriteLine(item.Element("day").Value);
        Console.WriteLine(item.Element("temp").Value);

        // 등등
    }
}


이렇게 코드를 짜면 복잡해질 수도 있는데 익명 객체를 활용하면 이를 해결할 수 있습니다.

static void Main(string[] args)
{
    string url = "https://xxxxx/xxx/";
    XElement xElement = XElement.Load(url);

    // data 태그를 모두 선택
    var xmlQuery = from item in xElement.Descendants("data")
                select new
                {
                    Hour = item.Element("hour").Value,
                    Day = item.Element("day").Value,
                    Temp = item.Element("temp").Value
                };
    
    foreach (var item in xmlQuery)
    {
        Console.WriteLine(item.Hour);
        Console.WriteLine(item.Day);
        Console.WriteLine(item.Temp);

        // 등등
    }
}


마지막으로 모델 클래스를 활용하여 처리할 수 있습니다.

class Time
{
    public string Hour { get; set; }
    public string Day { get; set; }
    public string Temp { get; set; }
}
static void Main(string[] args)
{
    string url = "https://xxxxx/xxx/";
    XElement xElement = XElement.Load(url);

    // data 태그를 모두 선택
    var xmlQuery = from item in xElement.Descendants("data")
                select new Whether()
                {
                    Hour = item.Element("hour").Value,
                    Day = item.Element("day").Value,
                    Temp = item.Element("temp").Value
                };
    
    foreach (var item in xmlQuery)
    {
        Console.WriteLine(item.Hour);
        Console.WriteLine(item.Day);
        Console.WriteLine(item.Temp);

        // 등등
    }
}


Invoke


Invoke는 다른 스레드에서 직접 접근할 수 없는 윈폼 컨트롤 작업에 대해 Invoke를 통해 작업 자체를 위임하여 MainThread가 해당 작업을 실행하게 합니다.


물음표(?) 연산자

null 연산자

널 조건 연산자는 ? 앞에 있는 객체가 NULL인지 체크해서 NULL이면 그냥 NULL을 리턴하고, 그렇지 않으면 ? 다음의 속성이나 메서드를 실행합니다.

// rows가 NULL이면 cnt 도 NULL
// rows가 NULL이 아니면 cnt는 실제 rows 갯수
int? cnt = rows?.Count;

// customers 컬렉션이 NULL이면 c는 NULL
// 아니면, c는 첫번째 배열요소
Customer c = customers?[0]; 

// customers가 널인지 체크하고
// 다시 customers[0]가 널인지 체크
int? age = customers?[0]?.Age;


?? 연산자

null을 반환해야하지만 리턴 변수가 null을 가질 수 없는 경우 어떻게 해야 할까요?

이 경우에 ??연산자를 함께 사용해서 null인 경우 디폴트 값을 리턴할 수 있습니다.

// rows가 NULL이면 cnt = 0
// 아니면 cnt는 실제 rows 갯수

int cnt = rows?.Count ?? 0;


이벤트 호출시 활용

C#에서 이벤트를 사용하는 루틴은 다음과 같습니다.

  1. 이벤트 필드를 로컬 변수에 할당
  2. 로컬 이벤트 변수가 null인지 체크
  3. 이벤트를 Invoke


위의 루틴을 Click2의 예로 간략히 표현할 수 있습니다.

Clicked를 별도의 임시 변수에 두지 않은 것은 ? 실행시 ? 이전 문장이 한번 실행되어 임시 변수에 자동 할당되기 때문입니다.

public class MyButton
{    
    public event EventHandler Clicked;

    // 이전 방식
    public void Click1()
    {
        //...

        // 스텝1. 임시변수에 이벤트 복사 (Thread safety 때문)
        var tempClicked = Clicked;
        // 스텝2. 널 체크
        if (tempClicked != null)
        {
            // 스텝3. 이벤트 Invoke
            tempClicked(this, null);
        }
    }

    // C# 6.0 방식
    public void Click2()
    {
        // ...

        // 위의 3 스텝을 널 조건 연산자을 사용하여
        // 한 문장으로 표현
        Clicked?.Invoke(this, null);
    }        
}


AssemblyInfo.cs

이 파일은 프로젝트와 관계되는 어셈블리의 정보가 담겨 있는 CS 파일입니다. 어셈블리는 하나의 기능을 생성하는 타입과 리소스의 집합체입니다. 하나의 프로젝트를 만든다는 것은 하나의 어셈블리를 만드는 것을 의미합니다.

버전 번호, 설명, 로드 된 어셈블리 등 응용 프로그램에 대 한 정보를 가져오기 위한 속성을 제공 합니다.


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

댓글남기기