C# and JAVA 2007. 5. 29. 11:27
http://genamics.com/developer/csharp_comparative_part10.htm 16. 선택문
C# 은 여러분이 정수형 , 문자 (char), 열거형 (enum) 혹은 (C++ 이나 Java 와는 달리 ) 문자열로 switch 문을 제어할 수 있도록 해준다 . Java 와 C++ 에서 여러분이 각 case 문장을 break 를 이용하여 빠져나올 경우 여러분은 이미 실행되고 있는 다른 case 문장에 대한 책임을 져야한다 . 나는 왜 이렇게 드물지만 에러를 유발할 수 있는 것을 Java 와 C++ 에서 기본 동작으로 만들어 놓았는지 모르겠지만 , C# 에서는 이렇게 하지 않는 것을 보니 기쁘다 .
17. 기정의된 (predefined) 타입
C# 의 원형 타입들은 Java 에만 추가되어 있는 unsigned 만 제외하고는 기본적으로 모두 동일하다 . C# 에는 sbyte, byte, short, ushort, int, uint, long, ulong, char, float 과 double 이 있다 . 여기서 유일하게 놀라운 점은 최신 프로세서를 이용할 수 있는 12 바이트 “ 십진 ”(decimal) 부동 소수점 숫자가 있다는 것이다 .
공유하기
URL 복사 카카오톡 공유 페이스북 공유 엑스 공유
C# and JAVA 2007. 5. 29. 11:26
http://genamics.com/developer/csharp_comparative_part10.htm 15. 어트리뷰트 (Attributes)
C# 과 Java 둘 모두 컴파일된 코드 내에는 필드에 대한 접근수준과 같은 정보를 포함한다 . C# 은 이러한 특성을 일반화하여 단순히 여러분이 클래스나 메소드 , 필드 혹은 심지어 각각의 파라미터와 같은 요소들에 관하여 사용자가 직접 입력한 정보들도 컴파일할 수 있다 . 이러한 정보들은 런타임시에 검색할 수도 있다 . 아래에 어트리뷰터를 이용하는 클래스에 대한 매우 간단한 예제가 나와 있다 :
[AuthorAttribute ("Ben Albahari")] class A { [Localizable(true)] public String Text { get {return text; } ... }}
Java 는 /** */ 와 주석을 결합하여 클래스와 메소드에 관한 추가정보들을 포함시키는데 , 그러나 이러한 정보들은 바이트 코드에 내장되지 않는다 . C# 은 미리 정의되어 있는 어트리뷰트인 ObsoleteAttribute 를 사용하여 컴파일러가 쓸모없는 코드 (obsolete code) 에 대하여 경고할 수 있도록 하며 , 그리고 ConditionalAttribute 를 사용하여 조건부 컴파일을 가능하게 할 수도 있다 . 마이크로소프트의 새로운 XML 라이브러리는 어트리뷰트를 활용하여 필드가 XML 로 직렬화되는 방식을 표현하는데 , 이는 여러분이 클래스를 쉽게 XML 로 변환한 다음 다시 그것을 재구성할 수도 있다는 것을 의미한다 . 어트리뷰트를 사용하는 또 다른 적절한 예는 제대로 강력한 클래스 탐색도구를 만드는 것이다 . C# 언어 레퍼런스는 어트리뷰트를 만들고 사용하는 방법에 관해 정확하게 기술하고 있다 .
C# and JAVA 2007. 5. 29. 11:23
http://genamics.com/developer/csharp_comparative_part9.htm 14. 파라미터 한정자 (Parameter Modifiers)
“ref” 파라미터 한정자
C# 은 (Java 와는 반대로 ) 여러분이 파라미터를 참조로 전달할 수 있도록 해준다 . 이러한 점을 보여주는 가장 극명한 사례는 바로 일반목적용 swap 메소드이다 . C++ 과는 달리 여러분은 ref 파라미터를 받는 메소드를 호출할 때는 물론 선언할 때에도 지정해 주어야만 한다 :
public class Test { public static void Main () { int a = 1; int b = 2; swap (ref a, ref b); } public static void swap (ref int a, ref int b) { int temp = a; a = b; b = temp; }}
“out” 파라미터 한정자
“out” 키워드라는 것도 있는데 , 이것은 ref 파라미터 한정자 를 그대로 구현한 것이다 . ref 한정자가 값이 메소드에 전달되기 전에 확실히 할당되어야 하는데 반해 out 한정자 는 리턴하기 전에 메소드의 구현이 확실히 파라미터에 값을 할당할 것을 필요로 한다 .
“params” 파라미터 한정자
params 한정자 는 메소드의 가장 마지막 파라미터에 추가되어 메소드가 특정 타입의 임의 개수의 파라미터를 받아들일 수 있도록 한다 . 예를 들자면 이런 것이다 :
public class Test { public static void Main () { Console.WriteLine (add (1, 2, 3, 4).ToString()); } public static int add (params int[] array) { int sum = 0; foreach (int i in array) sum += i; return sum; }}
여러분이 Java 를 배울 때 가장 놀라게 되는 것 중의 하나는 값을 참조로 전달할 수 없다는 점이다 . 이것은 여러분이 조금 지나서 이러한 기능을 사용할 필요성을 별로 느끼지 못하고 , 또 그러한 기능을 사용하지 않는 코드를 작성하기 때문이다 . 필자가 C# 의 스펙을 처음으로 다루었을 때 종종 “ 왜 사람들이 이게 없으면 안된다고 할까 ? 나는 이것 없이도 코드를 작성할 수 있는데 말야 ” 라고 생각하곤 했다 . 조금 자기반성해 보자면 , 나는 이러한 별로 유용하지 않은 기능을 제공하는 것이 진정한 논쟁거리가 아니라 여러분이 그것 없이 코드를 작성해 나갈 수 있느냐가 더 큰 논쟁거리임을 깨달았다 .
여러분이 C++ 에서 했던 것을 생각해 보았을 때 Java 가 파라미터를 전달하는 방법을 간소화 한 것은 매우 잘한 일이다 . C++ 에서 메소드의 파라미터와 메소드 호출은 값이나 참조 , 추가적으로 포인터를 가지는 것들로 전달되는데 , 이것은 불필요하게 복잡한 코드로 만들 수 있다 . C# 은 메소드와 메소드 호출 둘 모두 명시적으로 참조에 의해 전달될 수 있도록 하며 이는 혼동을 대폭 줄여주어 Java 의 목표와 동일한 목표를 달성할 수 있도록 해주는 반면 훨씬 더 풍부한 표현력을 제공해 준다 . C# 의 테마는 프로그래머가 어떠한 일을 수행하는 데 있어 빙빙 돌아서 해결해야 하는 상황으로 치닫지 않게끔 하는데 있다 . “ 참조에 의한 전달 ” 문제를 극복하는 것을 제시하는 Java 튜토리얼을 떠올려 보면 여러분은 1 개의 요소를 가진 배열이 여러분의 값을 갖도록 전달해야 할까 , 아니면 다른 클래스가 그 값을 갖도록 해야 할까 ?
C# and JAVA 2007. 5. 29. 11:11
13. 버전관리 (Versioning)
버전관리 문제를 해결하는 것은 .NET 프레임워크의 주요 고려사항이었다 . 이러한 고려사항의 대부분은 어셈블리 ( assemblies ) 에 해당 된다 . 그러한 것들 중에는 동일한 프로세스에서 여러버전의 동일한 어셈블리들을 실행하는 것과 같은 꽤 강력한 특성이 몇 가지 있다 .
C# 언어는 새로운 버전의 코드 ( 거의 대부분 닷넷 라이브러리 ) 가 만들어질 때 발생하는 소프트웨어 실패를 방지한다 . C# 언어 레퍼런스에서 이것에 관해 자세하게 설명하고 있지만 , 나는 그러한 문제를 하나의 상당히 함축한 예로 요약해 보겠다 .
Java 에서 JVM 과 함께 배포된 B라 는 클래스를 파생하는(derive) D 라는 클래스를 배포한다고 가정해 보자 . D 클래스는 foo 라는 메소드를 하나 갖고 있는데 , B 가 배포될 당시에는 B 에는 그 메소드가 포함되어 있지 않았다 . 나중에 B 가 업데이트되어 이제는 B 클래스에 foo 라는 메소드를 포함하게 되었으며 , D 클래스를 이용하는 소프트웨어를 실행하는 컴퓨터에 새 VM 이 설치되었다 . D 클래스를 이용하는 소프트웨어는 오작동을 일으킬 지도 모르는데 , 왜냐하면 B 클래스의 새로운 구현체는 D 클래스에 대하여 가상 호출 (virtual call) 을 하여 B 클래스에서는 전적으로 의도하지 않은 구현에 대한 행위를 수행할 것이기 때문이다 . C# 에서는 D 클래스의 foo 메소드는 override 한정자 ( 프로그래머의 의도를 표현하여 ) 없이 선언될 수도 있었기 때문에 , 런타임은 D 클래스의 foo 메소드가 B 클래스의 foo 메소드를 재정의하지 않고 숨겨야 함을 알게 된다 .
C# 레퍼런스 문서로부터 한가지 흥미로운 것을 인용해본다 : “C# 은 이러한 버전관리 문제를 개발자들로 하여금 그들의 의도 를 명확히 하게 하여 해결한다 ”. 비록 override 키워드를 사용하는 것이 의도를 나타내는 한 가지 방법이긴 하지만 한 메소드가 컴파일 시점에 재정의를 수행하는지를 ( 선언하기 보다는 ) 확인함으로써 컴파일러에 의해 자동적으로 생성되도록 할 수도 있다 . 이는 여러분이 여전히 Java 스러운 언어 (virtual 과 override 키워드를 사용하지 않는 ) 를 가지고도 버전관리 문제를 적절히 해결할 수 있음을 의미한다 .
필드 한정자 (Field Modifiers) 도 살펴보아라 .
C# and JAVA 2007. 5. 29. 11:06
http://genamics.com/developer/csharp_comparative_part8.htm 12. 인터페이스 (Interfaces)
C# 의 인터페이스는 Java 의 인터페이스와 유사하지만 더 높은 유연성을 갖고 사용될 수 있다 . 어떠한 클래스도 “ 명시적으로 ” 인터페이스를 구현할 수 있다 :
public interface ITeller { void Next ();} public interface IIterator { void Next ();} public class Clark : ITeller, IIterator { void ITeller.Next () { } void IIterator.Next () { }}
위에서 주어진 클래스는 두 가지 이점을 갖는다 . 첫째는 하나의 클래스가 이름 충돌 (naming conflicts) 을 걱정하지 않고도 여러 인터페이스를 구현할 수 있다는 것이다 . 두번째는 클래스가 그 클래스의 일반 사용자에게 유용하지 않을 경우 한 메소드를 “ 숨기도록 ” 해준다 . 명시적으로 구현된 메소드는 필요한 인터페이스로 클래스 형변환을 통해 호출될 수 있다 .
Clark clark = new Clark (); ((ITeller)clark ).Next();
C# and JAVA 2007. 5. 29. 11:05
http://genamics.com/developer/csharp_comparative_part8.htm 11. 다형성(Ploymorphism)
가상 메소드(virtual method)는 객체지향 언어가 다형성을 표현할 수 있도록 해준다. 이것은 파생 클래스가 기반 클래스에 있는 메소드와 동일한 시그너처를 가진 메소드를 작성할 수 있으며, 기반 클래스에서 파생 클래스의 메소드를 호출할 수 있다는 것을 의미한다. 기본적으로 Java에서는 모든 메소드가 가상이다. C#에서는 C++에서와 같이 virtual 키워드를 사용하여야만 메소드를 기반 클래스에서 호출될 것이다. C#에는 그뿐만 아니라 기반 클래스의 메소드를 재정의해야할(혹은 추상 메소드를 구현해야 하는) 메소드에는 override 키워드를 지정할 필요가 있다
Class B { public virtual void foo () {} }
Class D : B { public override void foo () {} }
메소드가 의도적으로 기반 클래스의 메소드를 숨김을 의미하는 “new” 키워드를 선언에 추가하지 않은채로 비가상 메소드를 재정의하려고 하게 되면 컴파일 타임 에러가 발생하게 될 것이다.
Class N : D { public new void foo () {} }
N n = new N (); n.foo(); // calls N's foo ((D)n).foo(); // calls D's foo ((B)n).foo(); // calls D's foo
C++과 Java에서와는 대조적으로 override 키워드를 필요로 하는 것은 소스코드를 볼 경우 어떤 메소드가 재정의 되었는지 좀 더 명확하게 알 수 있도록 해준다. 그러나 가상 메소드를 사용하는 것에는 나름 장점과 단점이 있다. 가상 메소드를 사용하는 것의 첫번째 장점은 가상 메소드를 명시하지 않는 것 보다 수행 속도가 조금 더 향상된다는 점이다. 두 번째 장점은 어떤 메소드가 재정의될지가 명확해 진다는 점이다. 그러나 이러한 장점이 곧 단점이 될 수 있다. 기본 옵션으로 Java에서 final 한정자를 두는 것과 C++에서 virtual 한정자를 두는 것을 비교해 보자. Java에서의 기본 옵션은 여러분의 프로그램이 다소 비효율적으로 실행되게끔 할 수도 있으며, 반면에 C++에서는 비록 예상치 못한 일일지라도 기반 클래스의 구현으로 인해 확장성이 제한될 수 있다.
C# and JAVA 2007. 5. 29. 10:57
http://genamics.com/developer/csharp_comparative_part7.htm 10. 연산자 오버로딩 (Operator Overloading)
연산자 오버로딩은 프로그래머가 자연스럽게 받아들일 수 있는 타입을 만들어 그것을 단순한 타입 (int, long 등 ) 처럼 사용할 수 있도록 해준다 . C# 은 C++ 보다는 좀 더 엄격한 버전의 연산자 오버로딩을 구현하긴 하지만 연산자 오버로딩의 전형적인 예와 같은 클래스와 복잡한 숫자 클래스도 잘 작동하도록 해준다 .
C# 에서 == 연산자는 레퍼런스로 비교되는 object 클래스의 비가상 (non-virtual; 연산자는 virtual 일 수 없다 ) 메소드이다 . 여러분이 클래스를 만들 때 여러분만의 == 연산자를 정의할 수도 있다 . 만약 여러분이 컬렉션을 가진 클래스를 사용할 경우에는 IComparable 인터페이스를 구현해야만 한다 . 이 인터페이스는 한 개의 구현해야할 메소드를 가지고 있는데 , CompareTo(object) 메소드로 불리며 “this” 가 더 크면 양수를 , 더 작으면 음수를 , 혹은 객체와 같은 값을 가질 경우 0 을 리턴해야 한다 . 또한 여러분이 만든 클래스를 사용하는 사람이 더 나은 문법을 갖기를 원한다면 <, <=, >=, > 메소드를 정의할 수도 있다 . 수치형 타입 (int, long 등 ) 은 IComparable 인터페이스를 구현한다 .
아래에는 동일성과 비교를 처리하는 방법의 예를 보여준다 :
public class Score : IComparable { int value; public Score (int score) { value = score; } public static bool operator == (Score x, Score y) { return x.value == y.value; } public static bool operator != (Score x, Score y) { return x.value != y.value; } public int CompareTo (object o) { return value - ((Score)o).value; }} Score a = new Score (5); Score b = new Score (5); Object c = a; Object d = b; a 와 b 를 레퍼런스로 비교하려면 : System.Console.WriteLine ((object)a == (object)b; // false a 와 b 를 값으로 비교하려면 : System.Console.WriteLine (a == b); // true c 와 d 를 레퍼런스로 비교하려면 : System.Console.WriteLine (c == d); // false c 와 d 를 값으로 비교하려면 : System.Console.WriteLine (((IComparable)c).CompareTo (d) == 0); // true 또한 여러분은 <, <=, >=, > 연산자를 추가하여 클래스의 점수를 매길 수도 있다 . C# 은 논리적으로 짝을 이루는 (!= 와 ==, > 와 <, >= 와 <=) 연산자가 둘 모두 정의되어야 함을 컴파일 시점에서 보장한다 .
C# and JAVA 2007. 5. 29. 10:55
http://genamics.com/developer/csharp_comparative_part6.htm 8. 구조체
“단지 여러분이 필요하기 때문에 정말로 효율적인 코드를 작성하기 위한 한가지 방법 ” 으로만이 아닌 C# 의 타입체계가 좀 더 우아하게 돌아가게 만들어주는 구성요소의 하나로서 C# 의 구조체를 살펴보는 것도 유익한 일이다 .
C++에서는 구조체와 클래스 ( 객체 ) 둘다 스택 / 인라인 혹은 힙에 할당될 수 있다 . C# 에서는 구조체는 항상 스택 / 인라인 영역에 생성되며 클래스 ( 객체 ) 는 항상 힙에 생성된다 . 구조체는 실제로 좀 더 효율적인 코드를 작성할 수 있도록 해준다 :
public struct Vector {
public float direction;
public int magnitude;
}
Vector[] vectors = new Vector [1000];
위 코드는 1000 개의 벡터 (Vector) 를 모두 한 곳에 집어넣을 공간을 할당할 것이며 , 우리가 벡터를 하나의 클래스로 선언하고 for 반복문을 이용하여 1000 개의 각 벡터를 인스턴스화하였으므로 훨씬 더 효율적이다 . 우리가 효율적으로 해낸 부분은 우리가 C#s 이나 Java 에서 정 수형 배열 (int array) 을 선언하는 것과 비슷하게 선언된 배열이다 :
int[] ints = new ints [1000];
C# 은 여러분이 언어에 내장되어 있는 원형 타입 (primitive) 들을 간단하게 확장할 수 있도록 해준다 . 사실 C# 은 모든 원형타입을 구조체로서 구현한다 . int 타입은 단순히 System.Int32 구조체의 별칭일 뿐이며 long 타입은 System.Int64 구조체 등의 별칭일 뿐이다 . 이러한 원형타입들은 당연히 컴파일러에서 특별하게 처리될 수도 있지만 언어 자체는 그러한 구분을 짓지는 않는다 . 다음 섹션은 C# 이 이러한 것들을 어떻게 이용하는지 보여줄 것이다 .
9. 타입 단일화 (Type Unification)
대부분의 언어는 원형타입 (int, long 등 ) 과 궁극적으로는 원형 타입들로 이루어진 고수준의 타입들을 가지고 있다 . 그것은 종종 원형타입과 고수준 타입들을 동일한 방식으로 다룰 수 있기 때문에 유용하다 . 예를 들어 정수 뿐만 아니라 문자열 모두를 가질 수 있는 컬렉션을 갖는 것이 유용할 때가 있다 . 스몰토크 (smalltalk) 는 어느정도의 효율성을 희생하고 int 와 long 을 String 이나 Form 으로 다루게 함으로써 이것을 가능하게 하고 있다 . Java 는 이러한 효율성을 희생하는 것을 피하여 C 나 C++ 에서와 같이 원형타입을 다루지만 각각의 원형 타입에 대응되는 -int 는 Integer 에 , double 은 Double 에 - 래퍼 (Wrapper) 클래스를 제공하고 있다 . C++ 의 템플릿 (template) 은 타입에 대한 연산을 그 타입에서 제공하는 한 어떠한 타입도 받아들이도록 작성된 코드를 짤 수 있도록 해준다 .
C#은 이러한 문제에 대하여 색다른 해결책을 제공한다 . 이전 섹션에서 필자는 어떻게 int 와 같은 원형 타입이 단순히 구조체에 대한 별칭일 뿐이었는지를 설명하면서 C# 의 구조체를 소개하였다 . 이는 구조체가 object 클래스가 가진 모든 메소드를 갖기 때문에 다음과 같은 코드를 짤 수 있도록 해준다 :
int i = 5;
System.Console.WriteLine (i.ToString());
만약 구조체를 객체로 사용하고 싶을 경우 , C# 은 구조체를 객체로 박싱할 것이며 다시 그것을 구조체로 사용하고 싶을 때에는 언박싱해줄 것이다 .
Stack stack = new Stack ();
stack.Push (i); // box the int
int j = (int) stack.Pop(); // unbox the int
구조체를 언박싱할 때 필요한 형변환과는 상관없이 이는 여러분이 구조체와 클래스간의 관계를 일관성 있게 처리하는 방법이다 . 여러분은 비록 CLR 이 박싱된 객체에 대한 별도의 최적화를 제공해 줄지는 몰라도 박싱이 래퍼 객체를 양산하는 것을 수반한다는 것을 털어놓아야 할 것이다 .
C#의 설계자들은 설계 과정동안 템플릿을 고려해야만 했다 . 필자는 템플릿을 사용하지 않은 이유에 대해 두가지 주요 원인이 있었으리라 짐작하는데 , 그 첫번째는 설계가 난잡해 질 수 있다는 점이다 . 템플릿은 어려워서 객체지향적인 특성들을 뒤죽박죽 섞어놓을 수 있고 , 템플릿은 프로그래머에게 너무 많은 ( 혼란스러울 정도로 ) 설계 가능성을 열어주며 , 리플렉션 (reflection) 과 결합하기가 어렵다 . 두 번째 이유는 컬렉션과 같은 닷넷 라이브러리가 템플릿을 사용하지 않을 경우 템플릿은 그렇게까지 유용하지는 않을 것이라는 점이다 . 그러나 만약 닷넷 클래스에서 템플릿을 사용했다면 닷넷 클래스들을 사용하는 20개 이상의 언어들도 템플릿도 결합해야만 했을 것이며 , 이를 기술적으로 이뤄내기가 상당히 어려웠을지도 모른다 .
템플릿( 제네릭 ) 이 Java Community Process 에 의해 Java 언어에 포함할 것을 고려중이라는 것도 흥미로운 일이다 . 그렇게 되면 각 기업은 Sun 에서 “ 닷넷은 최소 공분모 신드롬 (lowest common denominator syndrome) 을 겪고 있다 ” 라고 말하고 Microsoft 는 “Java 는 교차 언어 (cross-language support) 를 지원하지 않는다 ” 라고 말할 때부터 서로의 곡조에 맞춰 노래를 부르기 시작한지도 모르겠다 .
(8월 10 일 수정 ) Anders Hejlsberg 의 인터뷰를 읽은 후로 위에서 제시했던 어려움 때문에 첫 번째 릴리즈는 아니지만 템플릿이 나타날 것이 임박해졌다 . 바이트 코드 (byte-code) 는 아니지만 중간언어 (IL) 스펙이 IL 코드가 템플릿을 표현 ( 리플렉션과 잘 어울릴 수 있도록 비파괴적인 (non-destructive) 방법으로 ) 할 수 있도록 작성되어 졌음을 살펴보는 것도 매우 재미있었다 . Java 에 제네릭을 도입하려고 고려중인 Java Community Process 에 관한 링크도 첨부한다 :
C# and JAVA 2007. 5. 29. 10:52
http://genamics.com/developer/csharp_comparative_part5.htm 6. 열거형 (Enums)
여러분이 C 언어를 모를 경우를 생각해서 설명하는 거지만 Enums 는 여러분이 객체의 집합을 지정할 수 있도록 해주는데 , 예를 들어 :
선언 :
public enum Direction {North, East, West, South};
사용법 :
Direction wall = Direction.North;
열거자는 꽤 쓸만한 구성요소 (construct) 이므로 혹시나 여러분은 왜 C# 에 그것들을 포함하기로 결정하지 않았는지에 대한 의문이 들지도 모르겠지만 왜 그럼 Java 는 그것들을 생략하기로 결정했을까 ? Java 의 경우 , 여러분은 다음과 같이 해야만 한다 .
선언:
public class Direction {
public final static int NORTH = 1;
public final static int EAST = 2;
public final static int WEST = 3;
public final static int SOUTH = 4;
}
사용법 :
int wall = Direction.NORTH;
Java 버전이 좀 더 많은 것들을 표현해 주는 것처럼 보이는 것이 사실임에도 불구하고 그것은 조금 형 안정적 (type-safe) 이지 못한데 , 왜냐하면 여러분이 뜻하지 않게 컴파일러가 잡아내지도 않게 하면서 wall 을 아무 정수값에 할당할 수 있도록 해주기 때문이다 . 정확하게 하기 위해서 Java 에 대한 필자의 경험으로는 몇 가지 추가적인 토큰을 작성하고 여기에서 나타나 있는 형 - 안전성이 결핍되어 나타나는 오류를 찾아내는 데 그렇게 많은 시간이 들지는 않았었음에도 불구하고 열거자가 있는 것이 좋다 . C# 의 한 가지 이점은 여러분이 디버깅하기가 놀랄만큼 좋다는 것인데 , 만약 여러분이 열거형들이 결합되어 있는 것을 가진 열거형 (enumeration) 에 중단점 (break point) 를 걸 경우 C# 은 여러분이 해독해야만 하는 숫자 대신 자동적으로 direction 을 해독하여 사람이 판독할 수 있는 출력결과를 여러분에게 보여준다 :
선언 :
public enum Direction {North=1, East=2, West=4, South=8};
사용 :
Direction direction = Direction.North | Direction.West;
if ((direction & Direction.North) != 0)
....
만약 여러분이 if 문에 중단점을 걸게 되면 여러분은 숫자 5 가 아닌 사람이 읽을 수 있는 버전의 direction 을 보게 될 것이다 .
Java 에서 열거형이 없는 가장 그럴듯한 이유는 여러분이 열거형 대신 클래스를 이용하여 그럭저럭 해낼 수 있기 때문이다 . 이전 섹션에서 필자가 언급했던 것처럼 단독적인 클래스를 갖고서는 세상의 특징을 표현하는 것은 물론이거니와 또 다른 구성요소와 함께 할 수 있는 것 역시 불가능하다 . “ 클래스로 할 수 있는 것이면 새로운 구성요소를 도입하지 마라 ” 라는 Java 의 철학이 가져다 주는 이점은 뭘까 ? 단순성 (simplicity) 이 아마도 가장 큰 이점으로 보여지는데 , 이는 짧은 학습곡선과 프로그래머로 하여금 어떤 일들을 하는 데 있어 여러가지 방식으로 생각해야만 하는 것을 방지해 준다 . 사실 Java 언어는 포인터와 헤더 파일을 제거하고 , 단일한 객체 상속 (single-rooted object hierarchy) 과 같은 단순함을 목적으로 여러가지 면에서 C++ 을 개선하여 만들어졌다 . 그러나 이러한 모든 간소화 (simplifications) 의 공통적인 측면은 Java 가 실질적으로 코드 작성을 더욱 단순하게 만들어준다는 점이다 . 다른 구성요소들을 제외하고 우리는 지금까지 열거자 , 프로퍼티 , 그리고 이벤트에 대해 살펴보았는데 , 이것들은 여러분의 코딩을 좀 더 복잡하게 만든다 .
7. 컬렉션과 Foreach 문
C# 은 for 반복문의 짧은 형태를 제공하는데 , 이것도 역시 컬렉션 클래스에 대한 일관성을 증가시키도록 도와준다 .
Java 나 C++ 의 경우 :
1. while (! collection.isEmpty()) {
Object o = collection.get();
collection.next();
...
2. for (int i = 0; i < array.length; i++)...
C#에서는 :
1. foreach (object o in collection)...
2. foreach (int i in array)...
C#의 for 반복문은 컬렉션 객체 ( 컬렉션을 구현하는 배열 ) 에서만 효과가 있을 것이다 . 컬렉션 객체는 열거형 객체 (Enumerator object) 를 리턴하는 GetEnumerator() 메소드를 갖고 있다 . 열거형 객체는 MoveNext 메소드와 Current 프로퍼티를 갖는다 .
// 내용이 꽤 됐네요... Java에서는 열거형과 enhanced for loop라고 해서 C#의 foreach 반복문과 같은 역할을 하는 문법이 1.5부터 추가되었죠.
C# and JAVA 2007. 5. 29. 10:49
원문 : http://genamics.com/developer/csharp_comparative_part4.htm 5. 이벤트
C# 은 직접적으로 이벤트를 지원한다 . 비록 이벤트 처리가 프로그래밍이 시작된 이래로 프로그래밍에서 필수적인 부분이었다고는 하지만 대부분의 언어에서 이벤트의 개념을 정형화하고자 하는 노력은 놀랍게도 거의 없었다 . 만약 여러분이 오늘날의 주류를 차지하는 프레임워크에서 이벤트를 처리하는 방식을 살펴보게 된다면 델파이 (Delphi) 의 함수 포인터 ( 클로져 (closure) 라 불리는 ) 와 Java 의 내부 클래스 어댑터 , 그리고 물론 Windows API 의 메시지 시스템과 같은 선례를 찾을 수 있을 것이다 . C# 은 event 키워드와 함께 대리자 (delegates) 를 이용하여 이벤트 처리에 있어 매우 깔끔한 해결책을 제시한다 . 필자는 이를 보여주는 최선의 방법은 이벤트를 선언하고 , 일으키고 (fire), 처리하는 전체 과정을 보여주는 예제를 보여주는 것이라 생각한다 :
// 호출될 수 있는 메소드의 서명을 정의하는 위임 선언
public delegate void ScoreChangeEventHandler (int newScore, ref bool cancel);
// 이벤트를 만드는 클래스
public class Game {
// event 키워드를 사용하였음을 주목
public event ScoreChangeEventHandler ScoreChange;
int score;
// Score 프로퍼티
public int Score {
get {
return score;
}
set {
if (score != value) {
bool cancel = false;
ScoreChange (value, ref cancel);
if (! cancel)
score = value;
}
}
}
}
// 이벤트를 처리하는 클래스
public class Referee
{
public Referee (Game game) {
// 게임에서 점수가 언제 변하는 지를 관찰함
game.ScoreChange += new ScoreChangeEventHandler (game_ScoreChange);
}
// 이 메소드의 서명이 ScoreChangeEventHandler 의 서명과 어떻게 일치하는지 주목하라
private void game_ScoreChange (int newScore, ref bool cancel) {
if (newScore < 100)
System.Console.WriteLine ("Good Score");
else {
cancel = true;
System.Console.WriteLine ("No Score can be that high!");
}
}
}
// 모든 것들을 테스트 하는 클래스
public class GameTest
{
public static void Main () {
Game game = new Game ();
Referee referee = new Referee (game);
game.Score = 70;
game.Score = 110;
}
}
GameTest 에서는 게임을 하나 만들고 , 게임을 관찰하는 참조변수를 하나 만든 다음 , 참조 대상이 게임 점수가 변경되었을 때 어떻게 반응하는지를 확인해 보기 위하여 게임 점수를 변경한다 . 이 시스템에서 Game 은 참조변수에 대해 아무것도 알지 못하며 , 그리고 아무 클래스나 점수가 변경되는 것을 감시하고 변화에 대응하도록 해준다 . event 키워드는 모든 대리자의 메소드를 += 과 -= 과는 상관없이 그것이 선언되어 있는 클래스가 아닌 클래스로 숨긴다 . 이러한 연산자는 여러분이 원하는 만큼 여러 개의 이벤트 처리기를 이벤트에 추가하거나 제거할 수 있도록 해준다 .
여러분은 아마도 GUI 프레임워크에서 이러한 시스템을 처음 접하게 될텐데 , Game 은 사용자 입력에 따라 이벤트를 일으키는 UI 위젯에 해당되며 , 참조 대상은 이벤트를 처리하게 될 폼에 해당될 것이다 .
대리자는 Microsoft J++ 에서 처음으로 도입되었으며 , 또한 Anders Hejlsberg 에 의해 설계되었고 Sun 과 Microsoft 간의 수많은 기술적 , 법적 분쟁을 야기시켰다 . Java 를 설계했던 James Gosling 은 Anders Hejlsberg 에 관해 그의 델파이에 대한 애착이 그를 “ 메소드 포인터씨 (Mr. Method Pointers)” 로 만들었다라고 말하면서 농담을 건넸다 . Sun 에서 만들어진 대리자에 관한 논쟁을 검토한 후로 필자는 Gosling 을 “’ 모든 것은 클래스다 ’ 씨 ” 로 부르는 것이 공평하다고 생각한다 . 지난 몇 년간의 프로그래밍은 “ 현실을 잘 모델링하기 위해 추상화하는 것 ” 으로부터 많은 사람들에 의해 “ 현실은 객체지향이며 , 따라서 우리는 객체지향적인 추상화를 통해 모델링해야만 한다 ” 로 바뀌었다 .
대리자에 관한 Sun 과 Microsoft 의 논쟁은 아래에서 찾아볼 수 있다 :