= C# vs Java =
{{tag>C# Java}}
**Java 개발자를 위한 C#!**\\
* Java 개발자가 당장 C# 개발을 할 수 있도록 작성된 문서입니다.
* 모든 글은 "Java" 관점에서 서술합니다. 기본 문법 지식이 부족하면 이해하기 힘들 수 있습니다.
* 직접적인 **개발 적용**을 목적으로 하기 때문에 더 자세한 사항을 알고 싶다면 [[http://en.wikipedia.org/wiki/Comparison_of_C_Sharp_and_Java|여기]]를 참고하세요.
\\
= 출력문 Parameter 사용법 (string.Format) =
* https://msdn.microsoft.com/ko-kr/library/s8s7t687.aspx
//%d, %s 같이 지정할 필요없이 자동으로 형변환되어 출력 가능.
Console.WriteLine("{0} {1} {2}", 77, 3.141592, "Test");
//%05d, %.10f와 같은 표현
Console.WriteLine("{0:D5} {1:F10}", 77, 3.141592);
= Data Type =
Primitive/Reference Type 상관없이 모두 **소문자**로 시작!
== Primitive Types ==
* C#은 모든 Type이 객체!
* Java는 모든 Data Type이 부호 있는(Signed) 수이다. 그에 반해 C#은 부호 없는(unsigned) 수를 지원한다!
* byte는 signed를 지원한다.
^ Data Type ^ Size ^ Range ^
| sbyte | 1byte | -128 ~ 127 |
| ushort | 2byte | 0 ~ 65535 |
| uint | 4byte | 0 ~ 4294967295 |
| ulong | 2byte | 0 ~ 18446744073709551615 |
* Java와 마찬가지로 byte(1byte)를 지원하고, 공통적으로 char는 2byte이다.
* 큰 수를 다루는 Type에 차이가 있다. (Java는 BigDecimal(32byte), BigInteger(56bytes)가 존재)
^ Data Type ^ Size ^ Range ^
| Decimal | 16byte | ±1.0*1-e=28 ~ ±7.9*10e28 |
== 수↔문자열 메소드 ==
^ Java ^ C# ^
| Integer.parseInt(""); | int.Parse(""); |
| s.toString(); | s.ToString(); |
== 상수 ==
* Java는 **final**, C#은 **const** (C를 계승하는 듯.)
== Nullable 변수 ==
* Primitive Type 데이터에 아무 값도 가지지 않는 변수가 필요할 때 (선언만 한 것과는 다르다!)
* **Primetive Type**뒤에 **?**를 붙인다.
int? a = null;
int b;
int? c;
Console.WriteLine(a);
//Console.writeLine(b); /* Error! */
//Console.writeLine(c); /* Error! */
== var : Dynamic Type ==
* Javascript와 같은 "약한 Type 검사" 지원. 자동으로 컴파일러가 자동으로 데이터의 해당 형 변환.
* object와는 다르다.
== enum ==
=== int to Enum ===
* (MYENUM) Enum.ToObject(typeof(MYENUM), INTVALUE);
* ((MYENUM[]) Enum.GetValues(typeof(MYENUM)))[INTVAULE];
= Method 활용 =
**기본적으로 Java와 동일하게 Method의 Argument로 전달되는 값은 __Call by value__이다!**
단, C#은 키워드를 통해 Call by reference를 이용할 수 있다!
== 여러 개의 반환 값이 필요할 때 ==
* 아래 두 방법 모두 Parameter로 넘긴 값을 반환 받지 않고도 저장 가능하다!
=== Call by Reference ===
* 넘겨주는 쪽, 받는 쪽 모두 앞에 "**ref**" 키워드를 붙인다!
static void Main(string[] args)
{
int a = 10, b = 20;
Swap(ref a, ref b);
Console.WriteLine(a+" "+b);
}
static void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
=== 출력 전용 Parameter ===
* "Call by Reference"의 "**ref**" 키워드를 **"out"**으로 바꿔주면 된다.
* Write Only => 반드시 값을 저장해주어야 한다! => **안전하다!**
static void Divide(int x, int y, out int quotient, out int remainder)
{
quotient = x / y;
remainder = x % y;
}
static void Main(string[] args)
{
int x = 5;
int y = 3;
int q, r;
Divide(x, y, out q, out r);
Console.WriteLine(q+" "+r);
}
== Method 이름은 같고 Argument 개수가 다를 때 ==
=== Method Overloading ===
* Type이 다르거나 Argument 개수가 **유한**적으로 다를 때 사용. (Java와 동일)
=== Variable Length Arguments (가변 길이 매개변수) ===
* Type은 같으나 Argument 개수가 가변적인 경우 사용.
* Method argument에 **params TYPE[] args** 이용.
static void Main(string[] args)
{
int result = Sum(1, 2, 3, 4, 5);
Console.WriteLine(result);
}
static int Sum(params int[] args)
{
int sum = 0;
foreach (int n in args)
sum += n;
return sum;
}
== Argument 자동 매칭 및 초기화 ==
=== 명명된 Arguments ===
* **순서 상관 없이** Argument **이름**에 근거해서 데이터를 할당하는 기능
static void Main(string[] args)
{
PrintProfile(age : 33, name : "James");
}
static void PrintProfile(string name, int age)
{
Console.WriteLine(name+" / "+age);
}
=== 선택적 Arguments ===
* Argument에 Default값 지정으로 초기화 가능.
* 호출 시 Default값을 가진 Argument는 생략 가능.
* **항상 필수 매개 변수 뒤에 와야 한다!**
= Class 활용 =
* 상속, Overriding, this() 생성자 이용은 "**:**"를 이용한다. (C++ 방식)
* 상속, 방지는 "**sealed**". (= Java에서 **final**과 같음.)
== this() 생성자 ==
* 여러 생성자 안에 중복되는 코드 간략화
int a, b, c;
public MyClass()
{
this.a = 777;
}
public MyClass(int b) : this()
{
this.b = b;
}
public MyClass(int b, int c) : this(b)
{
this.c = c;
}
== 접근 한정자 ==
* public, protected, private는 Java와 동일.
* Java와 달리 접근 한정자 수식 안하면 **private**
* internal, protected internal은\\ 다른 어셈블리에 있는 코드에서는 private와 같은 수준의 접근성을 가지고, 같은 어셈블리(한 프로젝트에서 뽑아내는 결과물)에 있는 코드에 대해서만
* internal : **private**로 접근 가능. 다른 어셈블리는 접근 불가.
* protected internal : **protected**로 접근 가능. 상속 관계라면 다른 어셈블리도 접근 가능.
=== 객체 상속 Type 검사 ===
* is : bool 반환
* as : 성공하면 해당 객체를, 실패하면 null 반환. (당연하지만 Reference Type에만 사용 가능.)
Mammal mammal = new Dog();
Dog dog;
if(mammal is Dog)
{
dog = (Dog) mammal;
}
=== Method Overriding ===
* Java와 달리 한정자를 붙여야 되는 추가작업 필요.
* Overriding 될 부모 Class의 Method에 **virtual** 키워드 수식
* Overriding 할 자식 Class의 Method에 **override** 키워드 수식
=== Method 숨기기 ===
* 상속 관계에서 부모 클래스의 메소드를 감추고, 자식 클래스의 구현된 메소드만을 보여 주는 것.
* 자식 클래스에 **new** 키워드 수식
//다형성과 관계 있는 모습이 보인다.
//Base가 부모, Derived가 자식 관계일 때
//public new void MyMethod() {}
Derived d = new Derived();
d.MyMethod(); //자식
Base b = new Derived();
b.MyMethod();
== Paritial Class (분할 클래스) ==
* 클래스 구현이 길어질 경우 사용.
* **클래스 이름**만 **동일**하게 구성 후 기술, **partitial** 키워드를 이용한다.
== Extension Method (확장 메소드) ==
* C#에서 제공되는 기존 클래스의 기능을 확장하는 기법.
* static 클래스, static 메소드, 첫 번째 Parameter에 this를 지정하고, 다른 클래스에서 사용시 "using NAMESPACE"로 선언 후 접근한다.
public static class CLASSNAME
{
public static TYPE METHODNAME(this TYPE PARAM_NAME /*확장할 Class or Type*/, PARAM, ...)
{
//
}
}
using System;
using MyExtension;
namespace MyExtension
{
public static class IntegerExtension
{
public static int Square(this int myInt)
{
return myInt * myInt;
}
public static int Power(this int myInt, int exponent)
{
int result = myInt;
for (int i = 0; i < exponent; i++)
{
result *= myInt;
}
return result;
}
}
}
namespace ExtensionMethod
{
class MainApp
{
public static void Main(string[] args)
{
Console.WriteLine(3.Square());
Console.WriteLine(3.Power(3));
}
}
}
== Struct (구조체) ==
* 데이터를 담기 위한 자료구조.
* 클래스와 달리 **값 형식**으로 **깊은 복사**가 된다.
* 선언만으로도 인스턴스 생성됨.
* struct 키워드 이용.
= Interface 활용 =
* "파생(자식) 클래스가 어떤 메소드를 구현해야하는가?"를 정의.
* C#에서 네이밍 규칙으로 "I"로 시작한다.
* Method, **Event**, **Indexer**, **Property**만을 가질 수 있다.
* "**:**"를 Interface나 Class 이름 옆에 붙여 사용.
== Interface를 상속하는 Interface ==
* 아래의 경우 이용한다.
# 상속하려는 Interface가 소스 코드가 아닌 **Assembly로만 제공**되는 경우
# 상속하려는 Interface가 소스 코드를 갖고 있어도 **이미 Interface를 상속하는 Class들이 존재**하는 경우
== Abstract Class (추상 클래스) ==
* Interface와 Class 사이의 개념
* 구현을 갖되 **Instance**를 갖지 못한다.
= Generic Programming (일반화 프로그래밍) =
* Logic이 같은 데, **Type**만 다를 때 이용.
== Parameter의 Type 제약시키기 ==
GENERIC_METHOD/CLASS where T : CONSTRAINT
/* CONSTRAINT */
/*
struct : 값 형식
class : 참조 형식
new() : Parameter가 없는 생성자
기반_클래스_이름 : 기반 클래스의 파생 클래스
인터페이스_이름 : 인터페이스 구현 명시. (여러개 명시 가능)
U : 또 다른 Parameter "U"로부터 상속받은 클래스
*/
= 공통 기능이지만 다른 문법 =
== "Hello, World!"로 살펴보는 전체적 구조 ==
* C#
namespace HelloWorld {
class MainApp() {
public static void Main() {
Console.WriteLine("Hello World!");
}
}
}
* Java
class MainApp() {
public static void main() {
System.out.println("Hello World!");
}
}
=== C#의 소스는 Class가 최상위 개념이 아니다! ===
* C#은 Class 상위에 이들을 묶는 **namespace**라는 개념이 있다! (= namespace는 class의 집합체)\\ = C#의 기본 프로그램 단위는 Class가 아닌 **namespace**!\\ = Class(혹은 namespace) 이름과 Source file 이름이 같을 필요가 없다!
=== C#의 모든 Method 첫자는 "대문자"로 시작한다! ===
* Main() 쓸 때 헤깔린다.
* 특히 자동 완성(Intelligence) 이용할 때, 대소문자 구별안하고 타이핑하면 __작동이 안된다__! (이럴 때는 Eclipse가 그립다...)
== 배열 사용법 ==
* C#은 "[]"을 "Type"에만 붙이는 것을 허용!
* 다차원 구분은 "**,**"로 구분한다.
//int arr[] = new int[10]; /* Error! Java는 가능! */
int[] arr = new int[10]; /* 이것만 가능! */
//int[][] arr = new int[2][3]; /* Java */
int[,] = new int[2,3];
=== 가변 배열 (Jagged Array) ===
* 배열이 요소인 배열
int[][] arr1 = new int[3][];
arr1[0] = new int[]{ 1, 2, 3, 4, 5 };
arr1[1] = new int[]{ 10, 20, 30 };
arr1[3] = new int[]{ 400, 500 };
int[][] arr2 = new int[3][] { new int[]{ 1, 2, 3, 4, 5 }, new int[]{ 10, 20, 30 }, new int[]{ 400, 500 } };
=== foreach ===
int[] arr = {1,2,3};
for(int n : arr)
System.out.println(n);
int[] arr = {1,2,3};
foreach(int n in arr)
Console.WriteLine(n);
== Random ==
* C#은 seed 필요!
== Lambda ==
* Java는 **jdk1.8** 부터 사용 가능하다.
* C#은 **3.0**부터 사용 가능하다.
=== C# ===
* Anonymous Method(익명 메소드)를 만들기 위해 사용한다.(Delegate를 이용할 때보다 훨씬 간단해진다!)
* 람다식으로 만들어진 익명 메소드는 **Anonymous Function(무명 함수)**라고 한다.
==== Expression(식) Lambda ====
(PARAMETERS...) => EXPRESSION;
using System;
namespace ConsoleApplication1
{
class Program
{
delegate int Calc(int a, int b);
static void Main(string[] args)
{
Calc calc = (a, b) => a + b; //자동으로 Parameter Types 판단.
/* delegate를 이용하면 아래와 같은 표현이다. */
/*
Calc calc = delegate(int a, int b)
{
return a + b;
};
*/
Console.WriteLine(calc(5,6));
}
}
}
==== Statement(문) Lambda ====
(PARAMETERS...) => {
STATEMENTS;
...;
};
using System;
namespace ConsoleApplication1
{
class Program
{
delegate int BigValue(int a, int b);
static void Main(string[] args)
{
BigValue val =
(a, b) =>
{
return a > b ? a : b;
};
Console.WriteLine(val(10,20));
}
}
}
==== Func와 Action을 이용한 더 간편한 Anonymous Function 만들기 ====
* **별개의 Delegate를 생성하지 않고,** Anonymous Method/Function를 만들 수 있다.
===== Func Delegate =====
* **반환값이 __있는__ 경우** 사용한다.
* 기본적으로 1개의 Generic Type으로 Return Type을 갖고, 여러개일 경우 맨 마지막에 표현된다.
using System;
namespace ConsoleApplication1
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
/* Parameter 없이 반환값만 있는 경우 */
Func func1 = () => "Hello World";
Console.WriteLine(func1());
/* Parameter 및 반환값이 있는 경우 */
Func func2 =
(a, b) =>
{
int result = a > b? a : b;
return result;
};
Console.WriteLine(func2(10,20));
}
}
}
===== Action Delegate =====
* **반환값이 __없는__** 경우 사용한다.
* 기본적으로 어떤 Generic Type도 갖지 않는다. (반환값이 없기 때문.)
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
/* Parameter 없는 경우 */
Action act1 = () => Console.WriteLine("Hello World");
act1();
/* Parameter가 있는 경우 */
Action act2 =
(a, b) =>
{
int result = a > b? a : b;
Console.WriteLine(result+"가 더 크다!");
};
act2(10,20);
}
}
}
==== Expression Tree (식 트리) ====
* Code를 **Data**로 보관 가능.
* 파일에 저장
* Network를 통해 다른 Process에 전달
* Database Server에 보내어 실행 => [[#LINQ (Language INtegrated Query) | LINQ]]
== Data Structure (자료구조) ==
|<50%>|
^ ArrayList ^ LinkedList, List ^
^ HashTable ^ Dictionary ^
| Type 명시 불필요 (= object)\\ (System.Collections) | Type 명시 필요\\ (System.Collections.Generic) |
= C#에만 있는 문법 =
== Property ==
* 은닉성을 지키면서 getter/setter를 축약적으로 편리하게 이용할 수 있는 기능.
class 클래스이름
{
TYPE FIELD이름;
접근한정자 TYPE PROPERTY이름
{
get
{
return FIELD이름;
}
/* set 접근자를 구현하지 않으면 읽기 전용 */
set
{
FIELD이름 = value; //value는 암묵적 매개 변수
}
}
}
=== 자동 구현 Property ===
* get, set 구현 없이 그냥 "get; set;"으로 대체하면 된다. => 단순 반복 노동 감소!
* 3.0부터 사용 가능.
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyName mn = new MyName();
mn.Name1 = "James";
Console.WriteLine(mn.Name1);
mn.Name2 = "Luke";
Console.WriteLine(mn.Name2);
}
}
class MyName
{
//기본 Property 사용
private string _name;
public string Name1
{
get
{
return _name;
}
set
{
_name = value;
}
}
//자동 구현 Property 사용
public string Name2
{
get;
set;
}
}
}
=== Peroperty & Constructor (프로퍼티와 생성자) ===
* 프로퍼티의 생성자를 이용하여 Default 값 지정.
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyProfile mp = new MyProfile()
{
Name = "James",
Age = 77
};
Console.WriteLine("{0} {1}", mp.Name, mp.Age);
}
}
class MyProfile
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
}
=== Anonymous Type (무명 형식) ===
* 한 번 사용하고 다시는 사용하지 않을 인스턴스가 필요할 때 이용.
* 선언과 동시에 인스턴스 할당하고, 프로퍼티에 접근한다.
* **읽기 전용** (변경 불가)
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var x = new { Subject = "Math", Scores = new int[] {77,30,94,50} };
Console.WriteLine(x.Subject);
foreach (var scores in x.Scores)
{
Console.Write(scores+" ");
}
}
}
}
=== Property of Interface (인터페이스의 프로퍼티) ===
* 자동 구현 프로퍼티와 동일한 형태
* 당연한 얘기지만 이를 상속한 클래스는 반드시 모든 프로퍼티를 구현해야 한다.
* 자동 구현 프로퍼티를 이용하는 것도 가능하다.
=== Property of Abstract Class (추상 클래스의 프로퍼티) ===
using System;
namespace ConsoleApplication1
{
abstract class Product
{
private static int serial = 0;
public string SerialID
{
get
{
return String.Format("{0:d5}", serial++);
}
}
abstract public DateTime ProductDate
{
get;
set;
}
}
class MyProduct : Product
{
public override DateTime ProductDate
{
get;
set;
}
}
class Program
{
static void Main(string[] args)
{
Product p = new MyProduct()
{
ProductDate = DateTime.Now
};
Console.WriteLine(p.SerialID + "/" + p.ProductDate);
Console.WriteLine(p.SerialID + "/" + p.ProductDate);
}
}
}
==== Source 비교 ====
|<100%>|
^ Property 이용X ^ Property 이용O ^
|
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyProfile mp = new MyProfile();
mp.SetName("James");
mp.SetAge(77);
Console.WriteLine(mp.GetName()+"/"+mp.GetAge());
}
}
class MyProfile
{
private string name;
private int age;
public string GetName()
{
return name;
}
public void SetName(string value)
{
name = value;
}
public int GetAge()
{
return age;
}
public void SetAge(int value)
{
age = value;
}
}
}
| using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyProfile mp = new MyProfile
{
Name = "James",
Age = 77
};
Console.WriteLine(mp.Name+"/"+mp.Age);
}
}
class MyProfile
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
} |
== Indexer ==
* Property처럼 객체 내의 Data에 접근 할 수 있도록 하는 통로.\\ 다른점이라면 **Index**를 이용한다.
* 즉, **배열** Data의 getter/setter가 필요할 때 사용!
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyList list = new MyList();
for (int i = 0; i < 5; i++)
list[i] = i;
for (int i = 0; i < list.Length; i++ )
Console.WriteLine(i);
Console.WriteLine("index 3 : "+list[3]);
}
}
class MyList
{
private int[] arr;
public MyList()
{
arr = new int[3];
}
public int this[int idx]
{
get
{
return arr[idx];
}
set
{
//Resize
if (idx >= arr.Length)
{
Array.Resize(ref arr, idx + 1);
Console.WriteLine("Resized : " + arr.Length);
}
arr[idx] = value;
}
}
public int Length
{
get
{
return arr.Length;
}
}
}
}
=== foreach가 가능한 객체 만들기 ===
* 기본 예제로는 foreach가 사용 불가능!
* **IEnumerable**과 **Ienumerator**를 상속해야 한다!
using System;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyList list = new MyList();
for (int i = 0; i < 5; i++)
list[i] = i;
foreach (int e in list)
Console.WriteLine(e);
Console.WriteLine("index 3 : "+list[3]);
}
}
class MyList : IEnumerable, IEnumerator
{
private int[] arr;
int position = -1; //MoveNext() 때문!
public MyList()
{
arr = new int[3];
}
public int this[int idx]
{
get
{
return arr[idx];
}
set
{
//Resize
if (idx >= arr.Length)
{
Array.Resize(ref arr, idx + 1);
Console.WriteLine("Resized : " + arr.Length);
}
arr[idx] = value;
}
}
public int Length
{
get
{
return arr.Length;
}
}
/* 아래 3개의 Method는 IEnumerator의 Member */
public object Current
{
get
{
return arr[position];
}
}
public bool MoveNext()
{
if (position == arr.Length - 1)
{
Reset();
return false;
}
position++;
return position < arr.Length;
}
public void Reset()
{
position = -1;
}
/* 아래 Method는 IEnumerable의 Member */
public IEnumerator GetEnumerator()
{
/* "yield return"은 arr[i]를 반환하고, 그 중단된 위치에서 반복을 이어간다. */
for (int i = 0; i < arr.Length; i++ )
yield return arr[i];
}
}
}
== Delegate ==
* Method 자체를 Parameter로 넘기고 싶을 때 사용.
* Method에 의한 참조 => Delegate에 Method 주소 할당 후 이를 호출하면 그 Method 호출 됨.\\ (= Method 자체가 Parameter)
* Parameter에는 Method **이름**만이 들어간다.
* Delegate는 **Type**이다.\\ = Method 참조를 만들려면 Delegate의 Instance를 생성해야 한다.
* Callback을 구현하기 위해 사용. (중간의 대리/비서 역할)
ACCESS_MODIFIER delegate TYPE NAME(Param, ...);
=== 예제 ===
/* 결과를 출력한다는 공통된 기능을 갖고, 연산만 달라진다는 시나리오. */
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Calculator calc = new Calculator();
del_calc calc_plus = new del_calc(calc.Plus);
del_calc calc_minus = new del_calc(calc.Minus);
Print(calc_plus, 1, 2);
Print(calc_minus, 10, 20);
}
delegate int del_calc(int a, int b);
static void Print(del_calc c, int a, int b)
{
int result = c(a, b);
Console.WriteLine(result);
}
}
class Calculator
{
public int Plus(int a, int b)
{
return a + b;
}
public int Minus(int a, int b)
{
return a - b;
}
}
}
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//오름차순 버블 정렬
int[] arr1 = { 3, 5, 1, 0, 8 };
BubbleSort(arr1, new Compare(AscCompare));
foreach (int i in arr1)
Console.Write(i + " ");
Console.WriteLine();
//내림차순 버블 정렬
int[] arr2 = { 4, 99, 32, 45, 0 };
BubbleSort(arr2, new Compare(DescCompare));
foreach (int i in arr2)
Console.Write(i + " ");
}
delegate int Compare(int a, int b);
static int AscCompare(int a, int b)
{
if (a > b)
return 1;
else if (a == b)
return 0;
else
return -1;
}
static int DescCompare(int a, int b)
{
if (a < b)
return 1;
else if (a == b)
return 0;
else
return -1;
}
static void BubbleSort(int[] arr, Compare cpr)
{
int i = 0;
int j = 0;
int temp = 0;
for (i = 0; i < arr.Length - 1; i++ )
{
for (j = 0; j < arr.Length - (i + 1); j++)
{
if (cpr(arr[j], arr[j + 1]) > 0)
{
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
}
}
=== Delegate Chain ===
* 하나의 Delegate로 여러개의 Method를 연달아 호출해야 할 때 사용.
* Chain 생성 : "**+=**" 연산자를 이용하거나 "**Delegate.Combine()**"을 이용한다.
* Chain 제거 : "**-=**" 연산자를 이용하거나 "**Delegate.Remove()**"을 이용한다.
==== 예제 ====
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyDelegate dele1 = null;
dele1 += func1;
dele1 += func2;
dele1 += func3;
dele1 -= func2;
dele1();
Console.WriteLine();
MyDelegate dele2_1 = new MyDelegate(func1);
MyDelegate dele2_2 = new MyDelegate(func2);
MyDelegate dele2_3 = new MyDelegate(func3);
MyDelegate dele2_all = null;
dele2_all = (MyDelegate)Delegate.Combine(dele2_1, dele2_2, dele2_3);
dele2_all = (MyDelegate)Delegate.Remove(dele2_all, dele2_2);
dele2_all();
}
delegate void MyDelegate();
public static void func1()
{
Console.WriteLine("첫번째");
}
public static void func2()
{
Console.WriteLine("두번째");
}
public static void func3()
{
Console.WriteLine("세번째");
}
}
}
{{ :delegate2.png? |}}
\\
using System;
namespace ConsoleApplication1
{
class Program
{
Notifier notifier = new Notifier();
EventListener listener1 = new EventListener("listener1");
EventListener listener2 = new EventListener("listener2");
EventListener listener3 = new EventListener("listener3");
static void Main(string[] args)
{
Program program = new Program();
program.Example1();
program.Example2();
program.Example3();
}
public void Example1()
{
Console.WriteLine("*** Example1 ***");
notifier.EventOccured += listener1.SomethingHappend;
notifier.EventOccured += listener2.SomethingHappend;
notifier.EventOccured += listener3.SomethingHappend;
notifier.EventOccured("You've got mail.");
Console.WriteLine();
//끊기
notifier.EventOccured -= listener2.SomethingHappend;
notifier.EventOccured("Download Complete.");
Console.WriteLine();
}
public void Example2()
{
Console.WriteLine("*** Example2 ***");
notifier.EventOccured = new NotifyDelgate(listener2.SomethingHappend) + new NotifyDelgate(listener3.SomethingHappend);
notifier.EventOccured("Nuclear launch detected.");
Console.WriteLine();
}
public void Example3()
{
Console.WriteLine("*** Example3 ***");
NotifyDelgate noti1 = new NotifyDelgate(listener1.SomethingHappend);
NotifyDelgate noti2 = new NotifyDelgate(listener2.SomethingHappend);
notifier.EventOccured = (NotifyDelgate) Delegate.Combine(noti1, noti2);
notifier.EventOccured("Fire!");
Console.WriteLine();
//끊기
notifier.EventOccured = (NotifyDelgate) Delegate.Remove(notifier.EventOccured, noti2); //Delegate source, Delegate value
notifier.EventOccured("OK");
}
}
delegate void NotifyDelgate(string message);
class Notifier
{
public NotifyDelgate EventOccured;
}
class EventListener
{
private string name;
public EventListener(string name)
{
this.name = name;
}
public void SomethingHappend(string message)
{
Console.WriteLine(name+" / "+message);
}
}
}
{{ :delegate1.jpg? |}}
=== Anonymous Method (익명 메소드) ===
* **delegate** 키워드를 이용하여 선언.
* 어떤 메소드가 두 번 다시 사용할 일이 없다고 판단될 때 사용.
DELEGATE_INSTANCE = delegate(PARAMETER, ...)
{
//code
}
using System;
namespace ConsoleApplication1
{
class Program
{
delegate int del_calc(int a, int b);
static void Main(string[] args)
{
Print(
delegate(int a, int b)
{
return a * b;
}
, 5, 7);
}
static void Print(del_calc d, int a, int b)
{
Console.WriteLine(d(a,b));
}
}
}
=== Event ===
* 객체에 일어난 사건 알리기. Delegate의 주 용도.
using System;
namespace ConsoleApplication2
{
delegate void onClick(string what);
class Program
{
//event EventType EventName
public event onClick myClick;
public static void Main(string[] args)
{
Program main = new Program();
TestDelegate td = new TestDelegate();
onClick deleMethod1 = new onClick(td.MouseClick);
onClick deleMethod2 = new onClick(td.KeyBoardClick);
//Event += Delegate
main.myClick += deleMethod1;
main.myClick += deleMethod2;
main.myClick("Hello"); //두 Method에 전달됨.
}
}
public class TestDelegate
{
public void MouseClick(string what)
{
Console.WriteLine("Mouse Click! " + what);
}
public void KeyBoardClick(string what)
{
Console.WriteLine("KeyBoard Click! " + what);
}
}
}
/* Ver.1 */
using System;
namespace ConsoleApplication2
{
delegate void sendMessage(string message);
class Program
{
public static event sendMessage sendMessageEvent;
public static void Main(string[] args)
{
Program.sendMessageEvent += Program_sendMessageEvent;
Program.sendMessageEvent("Hello, World!");
}
static void Program_sendMessageEvent(string message)
{
Console.WriteLine(message);
}
}
}
/* Ver.2 */
using System;
namespace ConsoleApplication2
{
class Program
{
public static void Main(string[] args)
{
MyEventManager.sendMessageEvent += MyEventManager_sendMessageEvent;
MyEventManager.SetOnSendMessage("Hello World!");
}
static void MyEventManager_sendMessageEvent(string message)
{
Console.WriteLine(message);
}
}
class MyEventManager
{
public delegate void sendMessage(string message);
public static event sendMessage sendMessageEvent;
public static void SetOnSendMessage(string message)
{
if (sendMessageEvent != null)
sendMessageEvent(message);
}
}
}
=== Event vs. Delegate ===
* Event는 public 수식일지라도 자신이 선언된 클래스 외부에서 호출 불가.
* Delegate는 Callback 용도로, Event는 객체의 상태 변화 or 사건 발생을 알리는 용도로 구분하여 사용하는 게 옳다.
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyNotifier noti = new MyNotifier();
noti.Handler += new EventHandler(MyHandler);
//noti.Handler("test"); /* Error! event는 객체 외부에서 호출 불가. */
for (int i = 1; i < 30; i++)
noti.DoSomething(i);
}
static public void MyHandler(string message)
{
Console.WriteLine(message);
}
}
delegate void EventHandler(String message);
class MyNotifier
{
public event EventHandler Handler;
public void DoSomething(int num)
{
int temp = num % 10;
if (temp != 0 && temp % 3 == 0)
Handler(String.Format(num+" : 짝"));
}
}
}
== LINQ (Language INtegrated Query) ==
* 선행 지식으로 [[#Delegate]] & [[#Lambda]] 필요.
* Collection(배열등 여러 자료구조)을 편리하게 다루기 위한 목적. (Read, Filtering, Sort, Merge, ...)
* from 절에서 **IEnumerable** Type만 사용할 수 있다.
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int[] numbers = { 99, 2, 55, 1, 3, 77, 30, 20};
var result =
from n in numbers //어떤 집단?
where n % 2 == 0 //어떤 조건?
orderby n
select n;
foreach(int n in result)
Console.WriteLine(n);
}
}
}
= 명명법 =
= Visual Studio 2013 사용 Tip =
# Eclipse처럼 Method 자동완성(Intelligence) 사용시 곧바로 자동 소괄호+매개 변수 설명을 보고싶다면 ** Visual Assist X**를 사용하면 된다. (그런데 유료)
# Eclipse의 **syso, Ctrl+Space**를 치면 **System.out.println()**이 자동생성되는 것처럼 VS에도 같은 기능으로 **코드 조각**이라는 기능이 있다.
* 예) cw, Ctrl+Space => Console.WriteLine();
* 더 많은 예시는 https://msdn.microsoft.com/ko-kr/library/z41h7fat.aspx 참조.
= Eclipse vs Visual Studio (IDE 단축키) =
|<100% 20% 40% 40%>|
^ ^ Eclipse ^ Visual Studio ^
^ 자동 Package 추가\\ (Import/using) | Ctrl+Shift+O | (대소문자 정확히 입력 후 맨 끝에서)\\ Ctrl+.\\ Alt+Shift+F10 |
^ 자동 Interface & Abstract Class 요소 구현 | | (Header에 커서 두고)\\ Alt+Shift+F10 |
^ Method의 Argument 보기 | Ctrl+Space | Ctrl+Shift+Space |
^ 코드 자동 정렬 | Ctrl+Shift+F | Ctrl+K,F |
^ 줄 단위 Block | Shift+↑/↓ ||
^ 주석\\ (//) | 설정/해제 : Ctrl+/ | (코드 바로 앞에) 설정 : Ctrl+K+C, 해제 : Ctrl+K+U\\ (줄 맨 앞에) 설정/해제 : / |
^ 주석\\ (/* */) | 설정 : Ctrl+Shift+/\\ 해제 : Ctrl+Shift+\ | 설정/해제 : Shift+8 |
^ 줄 삭제 | Ctrl+D | Ctrl+Shift+L |
^ 줄 이동 | Alt+↑/↓ | (Block 미지정 상태에서)\\ Ctrl+X/L, Ctrl+V |
^ 줄 복사 | Ctrl+Alt+↑/↓ | (Block 미지정 상태에서)\\ Ctrl+C, Ctrl+V |