1.0
- 2002년 - C#1.0, Managed Code, Visual Studio 2002
- 어트리뷰트 : 속성을 쉽게 부여, 런타임시 필요한 정보들을 전달
[Conditional("DEBUG")] // DEBUG시 사용되는 메소드 [DllImport("User32.Dll")] //Win32 API DLL 사용시 [Obsolete("오래된 버전입니다.")] // 사용을 권장하지 않는 메소드 - 델리게이트 : 안전한 함수포인터, 연산자(+=, -=)
- 리플렉션 : 런타임에서 객체의 멤버를 조회하고 실행도 가능
- typeof 연산자 : 클래스 타입으로 접근할 때 사용 - System.Object의 GetType() : 객체명으로 접근할 때 ==> 객체.GetType(); - Type.GetType() : 네임스페이스로 접근할 때 사용 - Assembly.LoadFrom() : dll 파일에 직접 접근할 때 사용
- 값형&참조형간의 박싱 & 언박싱 : 최상위 객체 object 타입 소개, 스택에 값형, 힙에 참조형, 값을 객체로 포장시 boxing

- foreach : 반복문
- 프로퍼티 / 인덱서 : Get/Set 메서드 대신 직관적으로 사용
// Property private string name; public string Name { get { return "저는 " + name + "입니다"; } // 로직 구현도 가능 protected set { name = value; } // set에는 다른 접근제한 } // Auto Property (C# 3.0 이후 지원) public double Value { get; set; }// 인덱서를 this를 통해 구현 class MyClass{ private int[] array = new int[5]; public int this[int i] { get { return array[i]; } set { array[i] = value; } } } // 다른 클래스에서 사용시 MyClass myClass = new MyClass(); myClass[0] = 100; Console.WriteLine(myClass[0]);// 인덱서에서 첨자를 문자열을 사용 class NickName{ private Dictionary<string, string> names = new Dictionary<string, string>(); public string this[string realName] { get { return names[realName]; } set { names[realName] = value; } } } // 다른 클래스에서 사용시 NickName nickName = new NickName(); nickName["star"] = "KimStar"; Console.WriteLine(nickName["star"]);
1.1
- 2003년 - C#1.1, 1.0의 버그픽스
2.0
- 2005년 - C#2.0, ASP.NET 2.0, ADO.NET 2.0, Windows Form 2.0, Click Once, Generic
- 제네릭 : 어떤 타입이든 될 수 있는 타입
// 제네릭 이전 - ArrayList에는 object만 사용가능함 ArrayList oldList = new ArrayList(); oldList.Add(1); // boxing int a = (int)oldList[0]; // unboxing // 제네릭 이후 List<int> newList = new List<int>(); newList.Add(1); // boxing 안함 int b = newList[0]; // unboxing 안함
- 익명메소드와 클로저 : 코드블록을 독립적으로 사용할 수 있으며, 코드블럭에서 바깥의 지역변수 사용 가능
// 기존 위임자 사용시 void DisplayString(string s) { ... } delegate void MyDelegate(string s); MyDelegate func1 = new MyDelegate(DisplayString); MyDelegate func2 = DisplayString; // 익명메소드 사용시 MyDelegate func3 = new Mydelegate( delegate(string s) { Console.WriteLine("string"); } ) // 익명메소드 더 간단히 MyDelegate func4 = delegate(string s) { Console.WriteLine("string"); };int[] nums = new int[] {1,2,3,4,5,6,7,8,9,10}; int factor = 3; // 클로저 : 익명메소드 안에서 바깥의 factor를 사용 // Predicate : 조건을 정의 Predicate<int> isPowerOfNum = delegate(int num) { return num % factor == 0; }; int[] result = Array.FindAll(nums, isPowerOfNum); foreach (int num in result) { Console.Write(" {0}", num); } - 반복자 : IEnumerable<T> 는 값이 요청될때마다 구해서 가져옴
static void Main(string[] args) { int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; foreach (int num in GetPowerOfNum(nums, 2)) { Console.Write(" {0}", num); } } public static IEnumerable<int> GetPowerOfNum(int[] nums, int factor) { for (int i = 0; i < nums.Length; i++) { if (nums[i] % factor == 0) { Console.Write(" yield! "); // yield : iterator 블록에서 열거자 개체에 값을 제공하거나 반복이 끝났음을 알리기 위해 사용 yield return nums[i]; } } } 출력 : yield! 2 yield! 4 yield! 6 yield! 8 yield! 10 - Partial Type : 클래스를 여러 파일에 나눔,
- 널 가능타입 : 참조형은 null 선언 가능하지만, 값형은 빈값이라도 반드시 가졌어야 했음. null value propagation 지원 (a.b().c() 에서 b()가 null 이면 c()도 null)
// int인데 null 가능 int? num = null; // Null Value Propagation : null에 ToString메소드를 실행해도 예외없이 null Console.WriteLine(num.ToString()); 출력 : 없음
3.0
- 2006년 - WPF, WCF, WF, CardSpace
- 암시적인 타입을 적용한 지역변수: var 타입, 컴파일러가 타입을 유추
- 익명 타입 : LINQ 중간과정에 생기는 타입을 class로 정의하지 않고 익명타입으로 사용함..
int num = 2; string[] names = { "옴니아", "아이폰" }; // 암시적인 타입 : var를 통해 컴파일러가 타입을 유추 // 익명타입 : new 뒤에 빈칸 --> names의 값들을 받아서 임시로 타입을 만듬 // --> {num=2,name="옴니아",numName="옴니아 3"},{num=3,name="아이폰",numName="아이폰 4"} var pairs = names.Select(name => new { num = num++, name = name, numName = name + " " + num}).ToList(); foreach (var pair in pairs) { Console.WriteLine("num:{0}, name:{1}, numName:{2}", pair.num, pair.name, pair.numName); } 출력 : num:2, name:옴니아, numName:옴니아 3 num:3, name:아이폰, numName:아이폰 4 - 확장메서드 : 기존타입에 추가로 메소드를 추가할때 사용됨. LINQ 사용시 헬퍼메소드 개념으로도 사용.
public static class ExtensionExam { public static int GetMiddleNumber(this int[] nums) { int middleIndex = nums.Count() / 2; return nums.ElementAt(middleIndex); } } ... int[] nums = new int[] {1,2,3,4}; Console.WriteLine(nums.GetMiddleNumber()); 출력 : 3 - 객체와 컬렉션 초기화 생성자 : 생성자의 파라메터 대신, public 으로 선언된 멤버변수와 프로퍼티에 원하는 만큼 값을 할당 가능
public class Customer { public string name; public string address; } ... Customer test = new Customer(name="star", address="일산"); - 람다와 쿼리 표현식 : 람다는 익명메소드를 다듬은것, 쿼리표현식은 데이터 추출시 쿼리를 작성하는것.
int[] nums = new int[] {1,2,3,4}; // 쿼리표현식 IEnumerable<int> num1 = from num in nums where (num%2).Equals(0) orderby num descending select num; // 미리 정의된 메서드와 람다식의 조합 IEnumerable<int> num2 = nums.Where(num => (num%2).Equals(0)).OrderByDescending(num=>num); // num1과 num2는 같은 결과 - Expression Tree
- 자동으로 구현되는 프로퍼티
// 기존방식 private int num; public int Num { get {return num;} set {num = value; } } // C# 3.0 이후 자동구현 프로퍼티 public int Num2 { get; set; }
3.5
- 2007년 - LINQ, C#3.0 (람다식, 확장메서드 지원)
3.5 sp1
- 2008년 - ASP.NET Dynamic Data, Client Profile
4.0
- Application Compatibility & Deployment : 새로 만들어진 4.0 (2.0~3.5sp1까지는 2.0 기반) 이지만 2.0, 1.0과 호환
- Dynamic 타입의 추가
- Co-Contravariance : 역 공변성
- Named and Optional Parameters
public static int Add(int x, int y=8, int z=3) { return x+y+z; } ... Console.WriteLine(Add(2,6,9)); // x=2, y=6, z=9 Console.WriteLine(Add(2,6)); // x=9, y=2, z=3 Console.WriteLine(Add(2)); // x=9, y=8, z=3 Console.WriteLine(Add(2,z:6,y:9)); // x=2, y=9, z=6 Console.WriteLine(Add(y:2,x:9)); // x=9, y=2, z=3 Console.WriteLine(Add(9,z:1)); // x=9, y=8, z=1 - Parallel Computing
- .NET Framework Side By Side : 하나의 Process에서 여러버전의 Framework 사용가능
- TPL (Task Parallel Library) : PLINQ 사용
5.0
- Caller info attributes
- Windows Runtime support
- Asynchronous programming

비동기 프로그래밍
비동기 콜백함수가 많으면 Goto문을 많이 쓰는 것처럼 이해하기 힘든 스파게티 코드가 될 수 있다.
C# 5.0에서는 컨트롤의 흐름을 크게 변화하지 않고 동기를 비동기로 쉽게 변환할 수 있다.
- 동기 방식 : 5 line에서 download 동안 화면이 멈추고 사용자는 조작 불가한 상태가 된다.
Movie[] QueryMovies(int year, int first, int count) { var client = new webClient(); var url = string.Format(query, year, first, count); var data = client.DownloadString(new Uri(url)); var movies = from entry in XDocument.Parse(data).descendants(xa + "entry") ~~~ } - 비동기 방식 #1 : 백그라운드 쓰레드 방식으로 코드를 수정하여 비동기로 Donwload를 수행할 수 있다. 하지만 수정을 위한 로직의 변경이 많아진다.
Movie[] QueryMovies(int year, int first, int count) { var client = new webClient(); var url = string.Format(query, year, first, count); var data = client.DownloadString(new Uri(url)); var movies = from entry in XDocument.Parse(data).descendants(xa + "entry") ~~~ } - 비동기 방식 #2 : TaskAsync 방식으로 코드를 변경하면 기존의 로직에서 큰 변화 없이 비동기 처리가 가능하다.
async Task<Movie[]> QueryMovies(int year, int first, int count) // 2. 메소드로 async로 수정 { var client = new webClient(); var url = string.Format(query, year, first, count); var data = await client.DownloadStringTaskAsync(new Uri(url)); // 1. TaskAsync로 수정 var movies = from entry in XDocument.Parse(data).descendants(xa + "entry") ~~~ } // 호출하는 쪽 async void LoadMovies(int year) // 4. await 추가 { while(true) { var movies = await QueryMovies(~~~); // 3. 호출하는 곳에 await 추가 } }


