Section 3.5: 단락 평가 (Short-circuiting) 연산자

정의상으로 볼 때, 단락 평가 (short-circuiting) boolean 연산자는 첫번째 연산자가 전체 표현식 (expression) 의 결과를 결정지을 수 없는 경우에만 두번째 연산자의 실제 평가가 이루어지게 (evaluate) 된다.

다시말하면, 사용자가 && 연산자를 firstCondition && secondCondition 의 형태로 사용했다고 할 때, secondConditionfirstConditiontrue 인 경우에만 실제 평가가 이루어 지게 되며, 당연하게도 firstOperandsecondOperand 가 모두 true 로 평가될때만 전체 결과 역시 true 가 됨을 의미한다. 이는 여러 상황에서 유용하게 사용될 수 있는데, 예를 들어 list 가 세개 이상의 요소 (element) 를 가지고 있는지를 확인하여야 하는 상황에서, NullReferenceException 이 발생하는 상황을 피하기 위해 list 자체가 초기화가 되었는지도 확인이 필요한 상황을 가정해 보자. 이러한 경우, 아래와 같은 방법을 사용할 수 있다:

bool hasMoreThanThreeElements = myList != null && mList.Count > 3;

mList.Count > 3 조건 확인은 myList != null 조건이 만족하기 전까지는 수행되지 않을 것이다.

논리 AND 연산

&& 는 일반적인 boolean AND (&) 연산에 대응되는 단락 평가 (short-circuiting) 연산자이다.

var x = true; var y = false; x && x // true 를 반환한다. x && y // false 를 반환한다 (y 값이 평가된다). y && x // false 를 반환한다 (x 값이 평가되지 않는다). y && y // false 를 반환한다 (오른쪽 y 값은 평가되지 않는다).

Logical OR

|| 는 일반적인 boolean OR (|) 연산에 대응되는 단락 평가 (short-circuiting) 연산자이다.

var x = true; var y = false; x || x // true 를 반환한다 (오른쪽 x 값은 평가되지 않는다). x || y // true 를 반환한다 (y 값이 평가되지 않는다). y || x // true 를 반환한다 (x 와 y 값이 평가된다). y || y // false 를 반환한다 (두 y 값이 모두 평가된다).

사용 예제

if(object != null && object.Property) // object 가 null 인 경우 단락 평가 (short circuit) 로 인해 object.Property 는 절대 사용(access) 되지 않는다. Action1(); else Action2();
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 3.4: 묵시적 캐스팅 연산자와 명시적 캐스팅 연산자

C# 은 사용자 정의 타입들로 하여금 explicitimplicit 키워드를 이용해서 대입 (assignment) 과 캐스팅 (casting) 을 관리할 수 있도록 해 준다. 메소드의 시그니처 (signature) 는 아래와 같은 형식을 따른다:

public static <implicit/explicit> operator <ResultingType>(<SourceType> myType)

해당 메소드는 더 많은 파라미터를 받을 수도 없으며, instance 메소드로 만들 수도 없다. 그러나, 해당 메소드는 자신이 정의된 타입의 어떠한 private 멤버에게도 접근이 가능하다.

묵시적 및 명시적 캐스팅의 예제가 아래에 나와 있다:

public class BinaryImage { private bool[] _pixels; public static implicit operator ColorImage(BinaryImage im) { return new ColorImage(im); } public static explicit operator bool[](BinaryImage im) { return im._pixels; } }

위 예제 클래스는 아래와 같은 캐스팅 문법으로 사용 가능하다:

var binaryImage = new BinaryImage(); ColorImage colorImage = binaryImage; // 묵시적 캐스팅. 타입 정보가 주어지지 않았음을 주목한다. bool[] pixels = (bool[])binaryImage; // 명시적 캐스팅, 타입을 정의한다

캐스팅 연산자는 사용자의 타입으로부터 (from) / 그리고 사용자의 타입으로 (to) 의 양방향 모두에 대해 동작한다:

public class BinaryImage { public static explicit operator ColorImage(BinaryImage im) { return new ColorImage(im); } public static explicit operator BinaryImage(ColorImage cm) { return new BinaryImage(cm); } }

마지막으로, 타입 계층 (hierarchy) 내에서의 캐스팅에 사용될 수 있는 as 키워드는 이 상황에서 유효하지 않다. 명시적이나 묵시적인 캐스팅을 정의한다고 해도, 아래와 같은 작업을 수행할 수는 없다:

ColorImage cm = myBinaryImage as ColorImage;

위 시도는 컴파일 에러를 발생시킬 것이다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 3.3: 관계 연산자 (Relational Operator) 들

동등

주어진 피연산자들 (인자) 이 서로 동등한지를 확인한다.

"a" == "b" // false 를 반환한다. "a" == "a" // true 를 반환한다. 1 == 0 // false 를 반환한다. 1 == 1 // true 를 반환한다. false == true // false 를 반환한다. false == false // true 를 반환한다.

Java 와는 달리, 동등 비교 연산자는 string 에 대해 native 하게 동작한다.

동등 비교 연산자는 다른 타입들이 인자로 주어지더라도 해당 타입들간 묵시적 변환 (implicit cast)이 존재한다면 정상적으로 동작하게 되어 있다. 만약 적절한 묵시적 변환이 존재하지 않는다면, 사용자는 명시적 변환 (explicit cast) 을 수행하거나 호환 가능한 타입으로 변환해주는 메소드를 사용할 수 있다.

1 == 1.0 // int 에서 double 로의 묵시적 변환이 존재하므로 true 를 반환한다. new Object() == 1.0 // 컴파일되지 않는다. MyStruct.AsInt() == 1 // MyStruct 에 대해 AsInt() 를 호출하고 반환된 int 값을 1과 비교한다.

Visual Basic.NET 과 달리, 동등 비교 연산자는 동등 대입 연산자 (equality assignment operator) 와 같지 않다.

var x = new Object(); var y = new Object(); x == y // 피연산자들 (이 예제에서는 객체들) 이 각각 다른 참조값을 가지고 있으므로 false 를 반환한다. x == x // 양쪽 피연산자들이 모두 같은 참조값을 가지고 있으므로 true 를 반환한다.

대입 연산자 (=) 와 혼동하지 않도록 한다.

값 타입 (value type) 들에 대해서는, 동등 연산자는 양쪽 피연산자들의 값이 동등할 때 true 를 반환한다.
참조 타입 (reference type) 들에 대해서는, 양쪽 피연산자들의 (실제 값이 아니라) 참조값이 같은 경우 true 를 반환한다. 한가지 예외가 있다면 문자열 (string) 객체로써, 이는 값에 대한 동등 여부를 비교하게 된다.

비동등

주어진 피연산자들이 동등하지 않은지를 검사한다.

"a" != "b" // true 를 반환한다. "a" != "a" // false 를 반환한다. 1 != 0 // true 를 반환한다. 1 != 1 // false 를 반환한다. false != true // true 를 반환한다. false != false // false 를 반환한다. var x = new Object(); var y = new Object(); x != y // 두 피연산자가 각기 다른 참조값을 가지고 있으므로 true 를 반환한다. x != x // 두 피연산자가 모두 같은 참조값을 가지고 있으므로 false 를 반환한다.

이 연산자는 실제적으로 동등 (==) 연산자와 반대의 결과값을 반환하게 된다.

초과

첫번째 피연산자가 두번째 피연산자보다 더 큰지 검사한다.

3 > 5 // false 를 반환한다. 1 > 0 // true 를 반환한다. 2 > 2 // false 를 반환한다. var x = 10; var y = 15; x > y // false 를 반환한다. y > x // true 를 반환한다.

미만

첫번째 피연산자가 두번째 피연산자보다 더 작은지 검사한다.

2 < 4 // true 를 반환한다. 1 < -3 // false 를 반환한다. 2 < 2 // false 를 반환한다. var x = 12; var y = 22; x < y // true 를 반환한다. y < x // false 를 반환한다.

이상

첫번째 피연산자가 두번째 피연산자보다 더 크거나 같은지 검사한다.

7 >= 8 // false 를 반환한다. 0 >= 0 // true 를 반환한다.

이하

첫번째 피연산자가 두번째 피연산자보다 더 작거나 같은지 검사한다.

2 <= 4 // true 를 반환한다. 1 <= -3 // false 를 반환한다. 1 <= 1 // true 를 반환한다.
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 3.2: 동등 (equality) 연산자 재정의하기

동등 연산자 하나를 재정의 하는 것만으로는 충분치 않은 경우가 있다. 여러가지 다양한 상황에 따라, 아래에 소개된 내용들이 사용될 수 있다:

  1. object.Equalsobject.GetHashCode
  2. IEquatable<T>.Equals (선택 사항, 불필요한 boxing 을 피할 수 있게 해준다)
  3. == 연산자와 != 연산자 (선택 사항, 연산자를 사용할 수 있게 해준다)

Equals 를 재정의 하는 경우, GetHashCode 역시 재정의가 되어야 한다. Equals 를 실제로 구현하는 경우, 특수한 경우가 다수 존재한다: 다른 타입의 객체들 (object) 과 비교를 한다거나, 자기 자신과의 비교를 수행하는 경우 등이 이에 해당한다.

재정의가 되지 않은 경우, Equals 메소드와 == 연산자는 클래스와 구조체 (struct) 에 대해 각각 다르게 동작한다. 클래스들에 대해서는 단순히 reference 만을 비교하게 되며, 구조체들에 대해서는 속성 (property) 들에 대한 값을 (성능 저하를 유발할 수도 있는) reflection 을 이용해 비교하게 된다. == 는 재정의 되지 않는 한 구조체 비교를 위해서는 사용될 수 없다.

역주: 상황에 따라 byte 비교를 수행하는 경우와 reflection 을 이용한 field 비교를 수행하는 경우가 존재합니다. 보다 자세한 사항은 MSDN 을 참고하세요.

일반적으로 동등 연산자는 다음 규칙들을 준수해야 한다:

  • 예외 (exception) 를 throw 해서는 안된다.

  • 재귀성 (reflexivity) : A 는 언제나 A 자신과 동등해야 한다 (일부 시스템에서는 NULL 값에 이 규칙이 적용되지 않을 수 있다).

  • 전이성 (transitvity) : 만약 A 가 B 와 동등하며, B 가 C 와 동등하다면, A 는 C 와 동등하여야 한다.

  • 만약 A 가 B 와 동등하다면, A 와 B 는 동등한 hash code 값을 가진다.

  • 상속 트리 독립성 (Inheritance tree independence) : 만약 BCClass1 을 상속한 Class2 의 instance 라고 하면, Class1.Equals(A,B) 는 언제나 Class2.Equals(A,B) 와 동일한 값을 반환하여야 한다.

class Student: IEquatable < Student > { public string Name { get; set; } = ""; public bool Equals(Student other) { if (ReferenceEquals(other, null)) return false; if (ReferenceEquals(other, this)) return true; return string.Equals(Name, other.Name); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; return Equals(obj as Student); } public override int GetHashCode() { return Name ? .GetHashCode() ?? 0; } public static bool operator == (Student left, Student right) { return Equals(left, right); } public static bool operator != (Student left, Student right) { return ! Equals(left, right); } }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 3.1: 재정의 가능한 연산자들

C# 은 operator 키워드를 사용한 정적 (static) 멤버 함수를 선언하는 방식을 통해 사용자 정의 타입들로 하여금 연산자를 재정의 할 수 있는 기능을 제공하고 있다.
아래의 예제는 + 연산자를 구현하는 방법을 설명하고 있다.

아래와 같이 복소수 (complex number) 를 나타내는 Complex 라는 이름의 클래스가 존재한다고 했을 때:

public struct Complex { public double Real { get; set; } public double Imaginary { get; set; } }

이 클래스로 하여금 아래와 같은 + 연산자를 사용할 수 있는 추가적인 기능을 제공하고자 한다면,

Complex a = new Complex() { Real = 1, Imaginary = 2 }; Complex b = new Complex() { Real = 4, Imaginary = 8 }; Complex c = a + b;

이 클래스를 위해 + 연산자를 재정의 할 필요가 있을 것이다. 이는 operator 키워드를 사용한 정적 함수를 사용함으로써 구현 가능하다:

public static Complex operator + (Complex c1, Complex c2) { return new Complex { Real = c1.Real + c2.Real, Imaginary = c1.Imaginary + c2.Imaginary }; }

+, -, *, / 와 같은 연산자들은 모두 재정의가 가능하다. 또한, 동일한 타입을 반환하지 않는 연산자들 역시 재정의가 가능한 연산자 목록에 포함된다. (예를 들어, ==!= 는 boolean 값을 반환하지만 재정의가 가능하다) 아래에 소개된 짝 맞춤의 규칙은 여기에도 동일하게 적용된다.

역주: == 를 재정의 하는 경우 != 도 재정의해야 함을 의미하는 것으로 보입니다.

비교 연산자는 짝을 맞추어서 재정의 되어야 한다. (예: 만약 < 가 재정의 된다면, > 역시 재정의 되어야 한다).

재정의 가능한 연산자들의 전체 목록은 (재정의 불가능한 연산자들의 목록 및 몇몇 재정의 가능한 연산자들에게 해당하는 제약사항의 목록을 포함하여) MSDN - 재정의 가능한 연산자들 (C# 프로그래밍 가이드) 에서 확인할 수 있다.

is 연산자 재정의는 C# 7.0 의 Pattern Matching 기능과 함께 도입되었다. 더욱 세부적인 사항은 Pattern Matching 항목을 참고하라.

Cartesian 이라는 타입이 아래와 같이 주어졌다고 할 때,

public class Cartesian { public int X { get; } public int Y { get; } }

재정의 가능한 is 연산자는 극좌표 (Polar coordinates) 에 대해 아래와 같이 정의될 수 있다.

public static class Polar { public static bool operator is(Cartesian c, out double R, out double Theta) { R = Math.Sqrt(c.X * c.X + c.Y * c.Y); Theta = Math.Atan2(c.Y, c.X); return c.X != 0 || c.Y != 0; } }

이는 아래와 같은 형식으로 사용될 수 있을 것이다.

var c = Cartesian(3, 4); if (c is Polar(var R, *)) { Console.WriteLine(R); }

(위 예제는 Roslyn Pattern Matching Documentation 에서 가져온 예제이다)

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Chapter 3: 연산자 (operator) 들

파라미터 세부 사항
연산자 기호 (symbol) 재정의 (overload) 되는 연산자, 예: +, -, /, *
연산자 Type 재정의된 연산자가 반환할 type.
피연산자1 연산 수행에 있어 사용될 첫번째 피연산자.
피연산자2 이항 연산 수행시에 있어 사용될 두번째 피연산자.
구문 (statement) 결과를 반환하기 전에 연산 수행을 위하여 수행하여야 하는 선택적 (optional) 코드.

역주: 위 테이블은 연산자 재정의 (overloading) 시에 필요한 항목들을 나타낸 것으로 보입니다. 연산자 재정의에 대하여 더 알아보시려면 여기를 참조하세요.

C# 에서, 연산자 는 표현 (expression) 이나 구문 (statement) 내의 하나 이상의 피연산자들에게 적용되는 프로그램 요소이다.

하나의 피연산자를 받는 연산자 - 예를 들어 증감 연산자 (++) 혹은 new 등 - 들은 단항 연산자라고 불리운다. 두개의 피연산자를 받는 연산자 - 예를 들어 사칙연산 연산자들 (+,-,*,/) - 은 이항 연산자라고 불리운다. 또다른 하나의 연산자인 조건 연산자 (conditional operator ?:) 는 세개의 피연산자를 받으며 이는 C# 에서의 유일한 삼항연산자이다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 2.1: 각종 Literal 들

  • uint Literal
    uint literal 은 접미어 U 혹은 u 를 통해 정의되거나, 혹은 uint 범위 내의 정수값을 사용해 정의된다:
    uint ui = 5U;
  • int Literal
    int 간단히 int 범위 내의 정수값을 사용해 정의된다:
    int i = 5;
  • sbyte Literal
    sbyte 타입은 특별한 접미어가 따로 없다. int 로부터 Integer literal 이 묵시적으로 변환될 것이다:
    sbyte sb = 127;
  • decimal Literal
    decimal literal 은 실수 (real number) 에 접미어 M 혹은 m 을 붙여 정의된다:
    decimal m = 30.5M;
  • double Literal
    double literal 은 접미어 D 혹은 d 를 통해 정의되거나, 혹은 실수값을 사용해 정의된다:
    double d = 30.5D;
  • float Literal
    float literal 은 접미어 F 혹은 f 를 통해 정의되거나, 혹은 실수값을 사용해 정의된다:
    float f = 30.5F;
  • long Literal
    long literal 은 접미어 L 혹은 l 을 통해 정의되거나, 혹은 long 범위 내의 정수값을 사용해 정의된다:
    long l = 5L;
  • ulong Literal
    ulong literal 은 접미어 UL, ul, Ul, uL, LU, lu, Lu, 혹은 lU 를 통해 정의되거나, 혹은 ulong 범위 내의 정수값을 사용해 정의된다:
    ulong ul = 5UL;
  • string Literal
    string literal 은 쌍따옴표 " 를 이용해 값을 둘러싸는 방식으로 정의된다:
    string s = "hello, this is a string literal";
    string literal 은 문자열 이스케이프 시퀀스를 포함할 수 있다. 문자열 이스케이프 시퀀스 에 대한 설명을 참고한다.
    추가적으로, C# 은 축자 문자 문자열을 지원한다. (축자 문자 문자열 에 대한 설명을 참고한다). 이는 쌍따옴표 " 를 이용해 값을 둘러싸고 앞에 @ 을 덧붙이는 방식으로 정의된다. 축자 문자 문자열 내에 포함되어 있는 이스케이프 시퀀스는 무시되며, 포함된 모든 공백문자는 유효하게 처리된다:
    string s = @"The path is: C:\Windows\System32"; // 역슬래시 (backslash) 와 개행 문자 (newline) 가 문자열 내에 포함되어 있을 것이다
  • char Literal
    char literal 은 홑따옴표 ' 를 이용해 값을 둘러싸는 방식으로 정의된다:
    char c = 'h';
    character literal 역시 문자열 이스케이프 시퀀스를 포함할 수 있다. 문자열 이스케이프 시퀀스 에 대한 설명을 참고한다. character literal 은 (모든 이스케이프 시퀀스가 변환 처리된 이후) 정확히 한 글자만큼만의 길이를 가지고 있어야 한다. 비어있는 character literal 은 유효하지 않다. (default(char) 혹은 new char() 가 반환하는) 기본 character 는 '\0', 혹은 NULL 문자를 가지게 된다. (null literal 및 null reference 와 혼동하지 않도록 주의한다).
  • byte Literal
    byte 타입은 특별한 접미어가 따로 없다. int 로부터 Integer literal 이 묵시적으로 변환될 것이다:
    byte b = 127;
  • short Literal
    short 타입은 특별한 접미어가 따로 없다. int 로부터 Integer literal 이 묵시적으로 변환될 것이다:
    short s = 127;
  • ushort Literal
    ushort 타입은 특별한 접미어가 따로 없다. int 로부터 Integer literal 이 묵시적으로 변환될 것이다:
    ushort us = 127;
  • bool Literal
    bool literal 은 true 혹은 false 두 값 중에 하나를 가진다;
    bool b = true;
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 1.5: LinqPad 를 이용하여 새로운 Query 를 생성하기

LinqPad 는 .Net 언어들 (C#, F# and VB.Net.) 이 제공하는 기능들을 배우고 테스트 해볼 수 있는 환경을 제공하는 훌륭한 툴이다.

  1. LinqPad 를 설치한다.
  2. 새로운 Query 를 생성한다. ( Ctrl + N )
    New
  3. 언어 선택 항목에서, "C# statements" 를 선택한다.
    Csharp
  4. 아래 코드를 입력하고 실행 버튼을 클릭한다. ( F5 )
    string hw = "Hello World"; hw.Dump(); // 혹은 Console.WriteLine(hw);
    Code
  5. 결과 화면에 출력된 "Hello World" 메시지를 확인한다.
    HelloWorld
  6. 이제 첫번째 .Net 프로그램을 작성해 보았으니, LinqPad 에 포함된 샘플들을 "Samples" 브라우저를 통해 확인해 보도록 한다. .Net 언어들이 제공하는 수많은 기능들을 확인해 볼 수 있는 훌륭한 예제들이 준비되어 있다.
    Samples

참고 사항:

  1. "IL" 탭 클릭을 통해, 사용자의 .Net 코드가 생성하는 IL (Intermediate Language) 코드를 확인할 수 있다. 이는 학습을 위한 유용한 도구이다.
    IL
  2. LINQ to SQL 혹은 Linq to Entities 를 사용하는 경우, 생성되는 SQL 쿼리를 확인할 수 있다. 이는 LINQ 학습에 있어 또 하나의 훌륭한 방법이다.
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

+ Recent posts