Programming/C#

C# 3.0 변경점(1) - var, 자동 구현 속성, 객체/컬렉션 초기화, 익명 타입, 확장 메서드

lee308812 2019. 4. 3. 21:55

- C# 3.0으로 개발한 응용 프로그램은 .NET Framework 3.5이상에서 실행된다.

[ var 예약어 ]

- 타입 추론 기능이 추가되면서 메서드의 지역 변수 선언을 타입에 관계없이 var 예약어로 사용가능

- 컴파일 시점에 결정된다.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> numbers1 = new List<int>(new int[] { 1, 2, 3, 4, 5 });
            List<int> numbers2 = new List<int>(new int[] { 6, 7, 8, 9, 10 });

            Dictionary<string, List<int>> dict = new Dictionary<string, List<int>>();

            dict["first"] = numbers1;
            dict["second"] = numbers2;

            //foreach(KeyValuePair<string, List<int>> elem in dict)
            foreach (var elem in dict)
            {
                Console.WriteLine(elem.Key + " : " + Output(elem.Value));
            }
        }

        private static string Output(List<int> list)
        {
            StringBuilder sb = new StringBuilder();

            foreach(int elem in list)
            {
                sb.AppendFormat("{0},", elem);
            }

            return sb.ToString().TrimEnd(',');
        }
    }
}

[ 자동 구현 속성 ]

- 클래스의 멤버인 속성을 구현하다 보면 단순히 내부 필드와 일대일로 대응시켜 정의하는 경우가 생기는데, "자동 구현 속성"을 이용하면 이 경우를 단순하게 구현할 수 있다.

    class Person
    {
        public string Name { get; protected set; }
        public int Age { get; set; }
    }

 

[ 객체 초기화 ]

- 클래스의 내부 변수 수가 많아지면 다양한 생성자를 정의해야 하므로 코드가 길어진다.

- 번거로운 초기화 문제를 해결하기 위해, C#에서는 public 접근자가 명시된 멤버 변수를 new 구문에서 이름과 값을 지정하는 형태로 초기화하는 구문을 지원한다.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ConsoleApp1
{
    class Person
    {
        string _name;
        int _age;

        public Person()
        {
        }

        public Person(int age)
        {
            _age = age;
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person();
            Person p2 = new Person { Name = "Anders" };
            Person p3 = new Person { Age = 10 };
            Person p4 = new Person { Name = "Anders", Age = 10 };
            Person p5 = new Person(10) { Name = "Anders" }; // 생성자 + 객체 초기화
        }
    }
}

 

[ 컬렉션 초기화 ]

- 컬렉션 초기화 구문을 사용하면 new 구문과 함께 값을 설정하는 것이 가능하다.

- C# 컴파일러가 대신 Add 메서드를 호출하는 코드를 넣어준다. 따라서, 컬렉션 초기화 구문을 적용하려면 반드시 해당 클래스가 ICollection<T> 인터페이스를 구현해야한다. (ICollection<T> 인터페이스는 Add 메서드를 포함함)

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

            List<Person> list = new List<Person>
            {
                new Person {Name = "Ally", Age = 35},
                new Person {Name = "Luis", Age = 40},
            };
        }
    }
}

 

[ 익명 타입 ]

- C# 2.0에서 익명 메서드가 지원됐고, C# 3.0에는 타입에도 이름을 지정하지 않는 방식을 지원한다. 객체 초기화 구문에서 타입명을 제외하면 된다.

    class Program
    {
        static void Main(string[] args)
        {
            // 객체의 타입 명이 없으므로 var를 사용
            var p = new { Count = 10, Title = "Anders" };
            
            Console.WriteLine(p.Title + ": " + p.Count);
        }
    }

 

[ 부분 메서드 ]

- C# 2.0에서는 클래스만 지원되던 partial 예약어가 메서드까지 확장된 것이 "부분 메서드"이다.  

- 코드 분할은 안되고, 클래스가 partial class 여야만 하고, 메서드의 선언과 구현부만 분리할 수 있다.

- 다른 파일로 분리돼 있어도 되지만 반드시 동일한 프로젝트에 있어야 함.

using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    partial class MyTest
    {
        partial void Log(object obj);

        public void WriteTest()
        {
            this.Log("call test!");
        }
    }

    partial class MyTest
    {
        partial void Log(object obj)
        {
            Console.WriteLine(obj.ToString());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyTest t = new MyTest();
            t.WriteTest();
        }
    }
}

 

[ 확장 메서드 ]

- 기존 클래스의 내부 구조를 전혀 바꾸지 않고 마치 새로운 인스턴스 메서드를 정의하는 것 처럼 추가할 수 있다.

using System;

namespace ConsoleApp1
{
    // 확장 메서드는 static class에 정의되어야 함.
    static class ExtensionMethodSample
    {
        // 확장 메서드는 반드시 static이어야 하고,
        // 확장하려는 타입의 매개변수를 this 예약어와 함께 명시
        public static int GetWordCount(this string txt)
        {
            return txt.Split(' ').Length;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string text = "Hello, World!";

            Console.WriteLine("Count : " + text.GetWordCount());
        }
    }
}

- 사용 예로, System.Linq 네임스페이스에 정의된 Enumerable 타입에는 IEnumerable 인터페이스에 대한 각종 확장 메서드가 정의되어 있다. 이 중에서 Min 메서드는 목록에서 가장 낮은 값을 반환하는 확장 메서드다.

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{

    class Program
    {
        static void Main(string[] args)
        {
            List<int> list = new List<int>() { 5, 4, 3, 2, 1 };

            // IEnumerable의 Min 확장 메서드 호출
            Console.WriteLine(list.Min());
        }
    }
}