# 30 \~ 39

### 30 LINQ

닷넷에서는 특정 형식에 원래는 없던 기능을 덧붙이는 개념으로 확장 메서드(extension method)를 제공한다. `using System.Linq` 네임스페이스 선언하면 사용할 수 있다.

주요 메서드

* Sum()
* Count()
* Average()
* Max()
* Min()

#### 30.3 화살표 연산자와 람다 식으로 조건 처리

```cs
> Func<int, bool> isEven = x => x % 2 == 0;
> isEven(2)
true
> Action<string> greet = name => { var message = "Hello"; Console.WriteLine(message); };
> greet()
Hello
```

람다 식 자체는 하나의 함수고, 함수를 가리키는 함수 포인터이다.

**Where() 메서드로 `IEnumerable<T>` 형태의 데이터 가져오기**

```cs
> int[] numbers = { 1,2,3,4,5 }
> IEnumerable<int> newNumbers = numbers.Where(number => number > 3);
```

Where() 메서드에 람다 식을 제공하여 새로운 컬렉션을 가져올 수 있다.

**All()과 Any() 메서드로 조건 판단하기**

LINQ의 All()과 Any() 메서드는 배열 또는 컬렉션에서 모든 조건을 만족하거나 하나의 조건이라도 만족해야 하는 경우를 판단.

```cs
> bool[] completes = { true, false, true };
> completes.All(c => c == true);
false
```

All()은 조건식을 모두 만족해야 true

```cs
> bool[] completes = { true, false, true };
> completes.Any(c => c == false);
true
```

Any()는 하나라도 조건식을 만족하면 true

**Take()와 Skip() 메서드로 필요한 건수의 데이터 가져오기**

```cs
> var data = Enumerable.Range(0, 100);
> data.Take(5);
TakeIterator { 0,1,2,3,4 }
> data.Where(n => n % 2 == 0).Take(5);
TakeIterator { 0,2,4,6,8 }
```

Take()는 지정한 개수만큼 데이터 가져온다

```cs
> var data = Enumerable.Range(0, 100)
> var next = data.Skip(10).Take(5); // 10개를 제외하고 5개 가져오기
> next
TakeIterator {10,11,12,13,14}
```

Skip()은 지정한 수만큼 데이터를 제외한 컬렉션을 반환

**Distinct() 확장 메서드로 중복 제거하기**

```cs
> var data = Enumerable.Repeat(3,5);
> var result = data.Distinct();
> result
DistinctIterator {3}
```

#### 30.4 데이터 정렬과 검색

**OrderBy() 메서드로 문자열 컬렉션 오름차순 정렬하기**

```cs
> string[] colors = { "Red", "Green", "Blue" };
> var sortedColors = colors.OrderBy(name => name); // Blue, Green, Red
```

**OrderByDescending() 메서드로 문자열 컬렉션 내림차순 정렬하기**

```cs
> var colors = new List<string> { "Red", "Blue", "Green" };
> var sortedColors = colors.OrderByDescending(c => c); // Red Green Blue
```

**특정 문자열을 포함하는 컬렉션 가져오기**

```cs
> var colors = new List<string> { "red", "green", "blue" };
> var newColors = colors.Where(c => c.Contains("ee")); // green
```

**Single()과 SingleOrDefault() 확장 메서드**

Single()과 SingeOrDefault()는 컬렉션에서 조건에 맞는 값을 단 하나만 가져오는 확장 메서드이다.

* Single(): null 값이면 예외가 발생한다. 즉, 에러가 발생한다.
* SingleOrDefault(): 값이 없으면 null 값을 반환한다.

```cs
> string black = colors.Single(color => color == "Black"); // black 없으면 오류 남
> string black = colors.SingleOrDefault(color => color == "Black"); // black 없으면 null 반환
```

**First()와 FirstOrDefault() 확장 메서드**

컬렉션에서 첫 번째 요소를 가져온다.

* First(): 첫 번째 요소가 없으면 에러가 발생
* FirstOrDefault(): 첫 번째 요소가 없으면 기본값 반환

#### 30.5 메서드 구문과 쿼리 구문

* 메서드 구문: Where()같은 메서드를 사용하여 컬렉션 다루기
* 쿼리 구문: from, where, select 같은 키워드를 사용하여 쿼리 형태로 컬렉션 다루기

```cs
> numbers.Where(n => n % 2 == 0).OrderByDescending(n => n)
OrderedEnumerable<int, int> { 10,8,6,4,2 }
> (from n in numbers where n % 2 == 0 orderby n descending select n)
OrderedEnumerable<int, int> { 10,8,6,4,2 }
```

두 가지 문법으로 같은 결과를 낼 수 있다. 쿼리 구문에 메서드를 붙일 수도 있다.

#### 30.6 Select() 확장 메서드를 사용하여 새로운 형태로 가공하기

Select()는 컬렉션에서 새로운 형태의 데이터로 만들어 사용할 수 있다.

```cs
var nums = numbers.Select(n => n * n);

foreach( var num in nums)
```

Select()의 결괏값은 따로 클래스 이름이 정해지지 않은 익명 형식이기에 반드시 var과 함께 사용해야 한다.

```cs
// 점수가 80 이상인 학생의 이름만 선택
var highScorers = students
    .Where(s => s.Score >= 80)       // 필터링
    .Select(s => s.Name);            // 변환
```

* 단순히 조건을 적용해서 데이터를 골라낼 때는 `where()`만 사용하면 충분.
* 데이터를 변환하거나 새로운 형식으로 바꾸어야 할 때는 `select()`가 필요.

#### 30.7 ForEach() 메서드로 반복 출력하기

```cs
> var numbers = new List<int>() {10,20,30,40,50};
> numbers.Where(n => n <= 20).ToList().ForEach(n => Console.WriteLine(n));
10
20
```

for문이나 foreach문 사용 안 하고 각 요소에 대해 반복 돌릴 수 있다

### 32 개체 만들기

클래스 : class 키워드로 생성한 것 개체 : 클래스를 new 키워드를 사용해서 새로운 이름으로 만든 것

```cs
class ObjectNote
{
    public class Counter
    {
        public void GetTodayVisitCount()
        {
            Console.WriteLine("오늘 1234명이 접속");
        }
    }
    class Object
    {
        static void Main()
        {
            Counter counter = new Counter();
            counter.GetTodayVisitCount();
        }
    }
}
```

#### 32.3 인스턴스 메서드

인스턴스 메서드 : static 키워드가 붙지 않은 메서드. 호출하려면 클래스의 인스턴스를 생성하여 개체를 만들어야 한다.

```cs
public class MyMath
{
	public void Sum(int x, int y)
    {
    	int sum = x + y;
        Console.WriteLine($"합계 : {sum}");
    }
}

> MyMath myMath = new MyMath();
> myMath.Sum(3,5);
합계 : 8
```

#### 32.4 익명 형식

익명 형식 : 클래스를 선언하지 않고 개체 만들기. 특정 클래스 없이 이름 하나로 여러 속성을 모아 관리할 때 유용하다. 익명 형식을 만들 때 각 데이터 형식은 자동으로 유추해서 만든다.

```cs
> var hong = new { Name = "백승수", Age = 21 };
> $"이름 : {hong.Name}, 나이 : {hong.Age}"
"이름 : 백승수, 나이 : 21"
```

#### 32.5 정적 멤버와 인스턴스 멤버

static 키워드가 붙은 멤버(정적 멤버)에 접근할 땐 `클래스이름.멤버이름;` 형태로 접근하고, 안 붙은 멤버(인스턴스 멤버)에 접근할 땐 클래스의 인스턴스를 생성하고 `생성된 개체 이름.멤버이름;` 형태로 접근한다.

static이 붙으면 클래스 내의 여러 메서드에서 해당 클래스 변수를 공유해서 사용할 수 있다.

#### 32.7 ToString() 메서드 오버라이드

```cs
using System;

class My { }

class Your
{
    public override string ToString()
    {
        return "새로운 반환 문자열 지정";
    }

    class ToStringMethod
    {
        static void Main()
        {
            My my = new My();
            Console.WriteLine(my);

            Your your = new Your();
            Console.WriteLine(your);
        }
    }
}
```

**1. 클래스 선언**

```csharp
class My { }
```

* `My` 클래스는 아무 멤버도 없는 단순한 클래스입니다.
* `ToString()` 메서드를 재정의하지 않았기 때문에 기본 구현이 사용됩니다.

**2. `Your` 클래스와 `ToString()` 재정의**

```csharp
class Your
{
    public override string ToString()
    {
        return "새로운 반환 문자열 지정";
    }
}
```

* `Your` 클래스는 `ToString()` 메서드를 `override`하여 기본 동작을 변경했습니다.
* 이제 `Your` 객체를 문자열로 변환하려고 하면, "새로운 반환 문자열 지정"이 출력됩니다.
* `Main()` 메서드에서 두 개의 객체를 생성합니다:
  * `My` 클래스 객체: 기본 `ToString()` 구현을 사용.
  * `Your` 클래스 객체: `ToString()` 메서드를 재정의했으므로 재정의된 동작을 사용.
* `Console.WriteLine()`은 객체를 문자열로 출력하려고 할 때 `ToString()` 메서드를 호출합니다.

#### 32.8 클래스 배열

클래스의 인스턴스들도 배열에 넣을 수 있다.

#### 32.9 var 키워드를 사용하여 클래스의 인스턴스 생성하기

자료형 var로 하면 선언부 줄일 수 있다.

### 33 네임스페이스

닷넷은 System, System.Linq, System.Generic 등 많은 네임스페이스를 제공한다.

#### 33.1 네임스페이스

```cs
namespace 네임스페이스이름
{
	// 클래스 등의 정의가 들어옴
}
```

네임스페이스는 프로그램 규모가 커질 때 클래스 이름이 충돌하는 것을 방지하도록 클래스를 모아서 관리하는 개념이다. 네임스페이스를 달리하면 이름이 동일한 클래스를 하나의 프로젝트에서 사용할 수 있다.

```cs
namespace AllCar
{
	namespace MyCar {}
    namespace YourCar {}
}
```

네임스페이스는 중첩하여 사용할 수 있다.

```cs
using System;

namespace Foo
{
    public class Car
    {
        public void Go() => Console.WriteLine("[1] Foo 네임스페이스의 Car 호출");
    }
}

namespace Bar
{
    public class Car
    {
        public void Go() => Console.WriteLine("[2] Bar 네임스페이스의 Car 호출");
    }
}

class NamespaceNote
{
    static void Main()
    {
        Foo.Car fooCar = new Foo.Car();
        fooCar.Go();
        Bar.Car barCar = new Bar.Car();
        barCar.Go();
    }
}
```

사용 예제. 실제로 프로그램을 만들 땐 각 클래스를 다른 파일로 관리하는게 좋다.

#### 33.3 using 지시문

네임스페이스 사용하려면 using으로 선언해놔야됨.

```cs
using System;
using MyNamespace;
using AllCar.MyCar; // 계층형 네임스페이스
using Fe = AllCar.Ferrari; // 별칭 붙이기
```

### 34 필드 만들기

#### 34.1 필드

필드: 클래스 내에서 선언된 변수 또는 배열.

**필드 초기화**

클래스 내에 선언된 필드는 자동으로 해당 형식의 기본값으로 초기화된다.

* 정수형(int, long, byte, short) : 0
* 실수형(float, double) : 0.0
* 불형 : false
* 기타 참조형(reference type) : null

#### 34.2 액세스 한정자

액세스 한정자의 종류

* public : 모든 곳에서 접근 가능
* private : 현재 클래스 내에서만 접근 가능
* protected : 현재 클래스 또는 현재 클래스를 상속하는 자식 클래스에만 접근이 허가됨
* internal : 현재 프로젝트의 모든 클래스에 접근이 허가됨
* protected internal : 현재 어셈블리(DLL 파일) 또는 현재 어셈블리에서 파생된 모든 클래스에 접근이 허가됨

### 35 생성자

#### 35.1 생성자

클래스의 구성 요소 중엔 생성자(constructor)라는 메서드가 있다. 개체를 생성하면서 무엇인가를 하고자 할 때 사용되는 메서드.

일반적으로 생성자는 개체를 초기화하는 데 사용됨. 생성자 이름은 클래스 이름과 동일함.

클래스의 인스턴스가 생성될 때 자동으로 생성자가 실행된다. 클래스에 매개변수를 달리하여 생성자를 여러 개 만들 수도 있음. 생성자도 오버로드가 가능함. this 키워드를 사용해서 다른 생성자를 호출할 수도 있음.

**생성자를 사용하여 개체 생성하기**

```cs
class ConstructorDemo
{
    public ConstructorDemo()
    {
        Console.WriteLine("생성자 호출");
    }

    static void Main()
    {
        ConstructorDemo c = new ConstructorDemo();
    }
}
```

#### 35.2 매개변수가 있는 생성자 만들기

**매개변수가 여러 개인 생성자 만들기**

```cs
namespace ConstructorParameter
{
    class My
    {
        private string _name;
        private int _age;

        public My(string name, int age)
        {
            this._name = name;
            this._age = age;
        }
        public void PrintMy()
        {
            Console.WriteLine($"이름 : {this._name}, 나이 : {this._age}");
        }
    }
    class ConstructorParameter
    {
        static void Main()
        {
            My my = new My("백승수", 21);
            my.PrintMy();
        }
    }
}
```

**매개변수가 있는 생성자로 원의 넓이를 구하는 프로그램 만들기**

```cs
using System;

class Circle
{
    private int _radius;

    public Circle(int radius)
    {
        _radius = radius;
    }
    public double GetArea()
    {
        return Math.PI * _radius * _radius;
    }
}

class GetCircleArea
{
    static void Main()
    {
        Circle circle1 = new Circle(5);
        Console.WriteLine(circle1.GetArea());
        Circle circle2 = new Circle(10);
        Console.WriteLine(circle2.GetArea());
    }
}
```

#### 35.3 클래스에 생성자 여러 개 만들기

클래스에는 매개변수를 달리하여 생성자를 여러 개 만들 수 있다. 이를 생성자 오버로드라 한다.

```cs
class ConstructorLog
{
    public ConstructorLog()
    {
        Console.WriteLine("기본 생성자 실행");
    }

    public ConstructorLog(string message)
    {
        Console.WriteLine("오버로드된 생성자 실행 : " + message);
    }

    class ConstructorOverload
    {
        static void Main()
        {
            var log1 = new ConstructorLog();
            var log2 = new ConstructorLog("C#");   
            var log3 = new ConstructorLog("ASP.NET");
        }
    }
}
```

#### 35.4 정적 생성자와 인스턴스 생성자

클래스의 정적 멤버를 호출할 때 맨 먼저 호출되는 정적 생성자는 static으로 만들고, 인스턴스 생성자는 public 키워드로 만든다.

**정적 생성자 (static constructor)**

* 정적 생성자는 클래스의 정적 멤버가 처음 접근될 때 단 한 번만 호출됩니다.
* 인스턴스를 생성하지 않아도 호출됩니다.
* 클래스 전체에 걸쳐 단 한 번만 실행되므로, 클래스 레벨의 초기화 작업에 사용됩니다.
* 명시적으로 호출할 수 없으며, 매개변수를 가질 수 없습니다.

**인스턴스 생성자 (public constructor)**

* 인스턴스 생성자는 클래스의 객체를 생성할 때 호출됩니다.
* public, private, 또는 다른 접근 제한자를 가질 수 있습니다.
* 여러 개의 인스턴스 생성자를 만들어 오버로드할 수 있습니다.

**정적 생성자만 있을 때의 동작**

인스턴스를 생성할 수 없습니다. 정적 생성자는 인스턴스와 관계없이 클래스 레벨에서 단 한 번만 호출되므로, 인스턴스 생성자가 없으면 객체를 생성할 수 없습니다.

#### 35.5 this() 생성자로 다른 생성자 호출하기

생성자에서 this()는 자신의 또 다른 생성자를 의미함. this() 생성자로 매개변수가 있는 생성자에서 매개변수가 없는 생성자를 호출하거나 또 다른 생성자들을 호출할 수 있음.

```cs
> class Say
. {
.     private string message = "[1] hello.";
.     public Say() => Console.WriteLine(this.message);
. 
.     public Say(string message) : this()
.     {
.         this.message = message;
.         Console.WriteLine(this.message);
.     }
. }
> new Say("[2] byebye");
[1] hello.
[2] byebye
```

this() 호출 > this(string message) 호출 이런 순서로 가는 코드.

**생성자 포워딩**

this() 생성자를 사용하면 생성자를 포워딩(forwarding)하여 다른 생성자에 값을 전달하기 좋다.

```cs
> class Money
. {
.     public Money() : this(1000) { }
.     public Money(int money) => Console.WriteLine("Money : {0:#,###}", money);
. }
> var basic = new Money();
Money : 1,000
> var bonus = new Money(2000);
Money : 2,000
```

기본 생성자에 있는 `this(1000)`가 인자 있는 다른 생성자를 호출하는 예시.

#### 35.6 생성자를 사용하여 읽기 전용 필드 초기화

필드를 정의할 때 readonly 키워드를 붙여서 읽기 전용 필드를 만들 수 있다. 읽기 전용 필드는 클래스의 생성자로만 초기화가 가능하다.

상수는 선언과 동시에 반드시 초기화해야 에러가 발생하지 않지만, 읽기 전용 필드는 선언과 동시에 초기화도 가능하고 선언한 후 생성자로 초기화할 수도 있다.

### 36 소멸자

소멸자는 클래스에서 인스턴스화된 개체가 메모리상에서 없어질 때 실행되는 메서드이다.

#### 36.1 종료자

소멸자(destructor)는 종료자(finalizer)라고도 한다. 닷넷의 가비지 콜렉터에서 클래스의 인스턴스를 사용한 후 최종 정리할 때 실행되는 클래스에서 가장 늦게 호출되는 메서드이다.

클래스 이름과 동일한 메서드로 앞에 `~` 붙여서 만든다.

### 37 메서드와 매개변수

C# 에서는 모든 함수를 클래스 내에 선언하기 때문에, 함수 대신에 메서드라고 한다.

#### 37.1 메서드

메서드는 개체가 수행할 수 있는 기능, 동작, 행위 등을 의미한다. 메서드 이름은 다음과 같이 `동사+명사;` 형태를 권장한다. ex. GetPrice(), SetPrice()

#### 37.2 메서드의 매개변수 전달 방식

* 값 전달 방식: 말 그대로 매개변수로 값을 그대로 복사해서 전달
* 참조 전달 방식(ref): 실제 데이터는 매개변수가 선언된 쪽에서만 저장하고, 호출된 메서드에서는 참조만 하는 형태로 변수 이름만 전달
* 반환형 전달 방식(out): 메서드를 호출하는 쪽에서 선언만 하고, 초기화하지 않고 전달하면 메서드 쪽에서 해당 데이터를 초기화해서 넘겨주는 방식
* 가변형 전달 방식(params): 매개 변수 여러 개를 배열형으로 받을 때 `params` 키워드를 붙인다. 가변 길이 매개변수는 매개변수를 선언할 때 반드시 마지막에 위치해야 함.

**값 전달 방식**

```cs
void ModifyValue(int x)
{
    x = 10; // x의 값만 변경됨
    Console.WriteLine($"Inside Method: {x}");
}

int a = 5;
ModifyValue(a);
Console.WriteLine($"Outside Method: {a}");
```

그냥 변수 넣으면 메서드 안에서 해당 변수 조작 못한다

**참조 전달 방식**

```cs
void ModifyRef(ref int x)
{
    x = 10; // 원본 값 변경
    Console.WriteLine($"Inside Method: {x}");
}

int a = 5;
ModifyRef(ref a);
Console.WriteLine($"Outside Method: {a}");
```

ref 붙여서 변수 넣으면 메서드 안에서 해당 변수 조작 가능하다. 포인터랑 비슷한듯?

**반환형 전달 방식**

```cs
void InitializeOut(out int x)
{
    x = 10; // 반드시 초기화 필요
    Console.WriteLine($"Inside Method: {x}");
}

int a; // 초기화하지 않음
InitializeOut(out a);
Console.WriteLine($"Outside Method: {a}");
```

메서드한테 해당 변수 초기화까지 맡겨버린다. return은 반환을 하는거고, 이건 ref에 값 넣는 과정

**ref와 out의 차이**

ref: 메서드 호출 전에 반드시 변수 초기화해야 함. out: 메서드 호출 전에 초기화 안 해도 되고, 메서드 내부에선 반드시 초기화해야 함.

ref: ref는 입력으로도 사용되기 때문에 기존 값을 변경할 수 있다는 의미를 가짐. out: 출력 전용이므로 초기값은 중요하지 않고, 메서드 내부에서 값을 설정해서 반환하겠다는 의미를 가짐.

**날짜 형태의 문자열을 날짜형으로 변환: out 키워드 사용하기**

```cs
자료형.TryParse("변환할 내용", out "변환이 되면 담을 그릇")
```

```cs
if (DateTime.TryParse("2020-01-01", out var myDate))
{
	Console.WriteLine(myDate);
}
```

#### 37.3 가변 길이 매개변수

```cs
class Program
{
    // params 키워드를 사용한 메서드
    static int AddNumbers(params int[] numbers)
    {
        int sum = 0;
        foreach (int num in numbers)
        {
            sum += num;
        }
        return sum;
    }

    static void Main()
    {
        // 1. 개별적으로 값을 나열해서 전달
        int result1 = AddNumbers(1, 2, 3, 4, 5);
        Console.WriteLine($"Sum 1: {result1}");

        // 2. 배열로 값을 전달
        int[] numbersArray = { 10, 20, 30 };
        int result2 = AddNumbers(numbersArray);
        Console.WriteLine($"Sum 2: {result2}");
    }
}
```

params 쓰면 매개변수 여러 개 한꺼번에 보낼 수 있다.

#### 37.4 메서드 본문을 줄여 표현하기

**식 본문 메서드 사용하기**

```cs
static void Walk() => Console.WriteLine("Walk");
```

화살표 연산자를 이용하여 메서드 본문을 줄여 표현할 수 있다.

**단일 표현식의 메서드를 한 줄로 정의하기**

결괏값이 하나인 단일 표현식일 때는 화살표 기호로 메서드 본문을 줄여 표현할 수 있다.

```cs
static int AddAge(int age) => age + 1; // return age + 1의 축약 형식
```

#### 37.5 선택적 매개변수

```cs
> static int Add(int a, int b = 1)
{
	return a+b;
}
> Add(5)
6
> Add(5,3)
8
```

기본값 넣어주면 사용할 때 인자 넣어줘도 되고 안 넣어줘도 되고. 이걸로 메서드 오버로드한 효과 낼 수 있다.

**명명된 매개변수**

```cs
Console.WriteLine(Sum(second: 3, first: 2));
```

순서 상관 없이 매개변수에 값 직접 할당 가능

### 38 속성 사용하기

속성(property)은 필드 값을 읽거나, 쓰거나, 계산하는 방법을 제공하는 클래스 속성을 나타내는 멤버

#### 38.1 속성

프로퍼티: 괄호가 없는 메서드. private 성격이 있는 필드를 public 속성으로 외부에 공개할 때 사용.

```cs
public class Car
{
    private string name;
    public string Name { get { return name; } set { name = value; } }
}
Car c1 = new Car();
c1.Name = "Car1";
Console.WriteLine(c1.Name);
```

#### 38.3 자동으로 구현된 속성

```cs
class Car
{
	public string Name { get; set; }
}
```

이렇게 한 줄로 프로퍼티를 정의하는 걸 auto property라고 함.

#### 38.4 자동 속성 이니셜라이저

```cs
public static string Name { get; set; } = "길벗";
```

속성 선언과 동시에 기본값으로 초기화 가능

#### 38.5 읽기 전용 속성과 쓰기 전용 속성

```cs
public string Message { get; private set; } = "읽기 전용 속성";
```

set에 private을 사용하면 수정이 불가능한 읽기 전용 속성을 만들 수 있다.

#### 38.8 화살표 연산자로 속성과 메서드를 줄여서 표현하기

```cs
public int Count
{
	get => count;
    set => count = value;
}
```

#### 38.9 개체 이니셜라이저

```cs
> class Course
. { 
.     public int Id { get; set; }
.     public string Title { get; set; }
. }
> // 1. Initialize with property
> Course csharp = new Course(); csharp.Id = 1; csharp.Title = "C#";
> Console.WriteLine($"{csharp.Id} - {csharp.Title}");
1 - C#
> // 2. Inittialize with object initializer
> Course aspnet = new Course() { Id = 2, Title = "ASP.NET" };
> Console.WriteLine($"{aspnet.Id} - {aspnet.Title}");
2 - ASP.NET
```

개체 이니셜라이저를 이용하면 좀 더 쉽게 속성을 초기화할 수 있다.

```cs
public string Name { get; set; }
public int Age { get; set; }

public Person(string name, int age)
{
	Name = name; Age = age;
}

(...)

Person PC = new Person("백승수", 21);
```

개체를 생성할 때 생성자로도 속성을 초기화할 수 있다.

#### 38.11 nameof 연산자

```cs
class Car
{
	public string Maker { get; set; }
}
> Car car = new Car();
> Console.WriteLine(nameof(car.Maker));
Maker
```

nameof 연산자를 사용하면 속성이나 메서드 이름을 문자열로 가져올 수 있다.

#### 38.12 익명 형식

```cs
> var person = new { Name = "백승수", Age = 21 };
> person.Name
"백승수"
> person.Age
21
```

익명 형식을 사용하면 특정 클래스로 형식을 만들 필요 없이 간단히 객체를 생성해낼 수 있다.

#### 38.13 익명 형식과 덕 타이핑

"새인데 오리처럼 생겼고, 오리처럼 수영하며, 오리처럼 꽥꽥대면 나는 그 새를 오리라고 하겠다."

익명 형식으로 개체 만든 후에 속성과 형식이 다른 개체를 대입하면 오류 난다.

처음 개체가 만들어지면 그 형식과 동일한 형태로만 다시 할당되는 것을 덕 타이핑이라 한다.

![](https://velog.velcdn.com/images/biomatrix117/post/39763817-2f8e-4aee-9bde-b60c0e37c189/image.png)

> 덕 타이핑이란, “내가 정의한 동작을 할 수 있다면 그 타입으로 인정해주는 것이다.” 어렵게 말하면 “객체의 변수, 메소드의 집합이 객체의 타입을 결정하는 것”을 말한다.

사진을 보자. 당장 저것의 이름이 콘센트인지 돼지코인지는 중요하지 않다. 내가 정의한 동작(코드를 꽂을 수 있다!)를 실행할 수 있다면 콘센트로 간주할 수 있다는 것이다.

<https://velog.io/@thumb\\_hyeok/%EB%8D%95-%ED%83%80%EC%9D%B4%ED%95%91%EA%B3%BC-%EA%B5%AC%EC%A1%B0-%EA%B8%B0%EB%B0%98-%ED%83%80%EC%9E%85>

#### 38.14 생성자로 속성에 대한 유효성 검사 구현하기

프로그램을 작성하다보면 특정 속성은 반드시 특정 값으로 초기화해야 할 때가 있다. 즉, null 또는 빈 값이 들어오면 안 될 때가 있는데, 이때는 생성자를 사용하여 반드시 특정 값으로 넘겨주도록 강제할 수 있다.

```cs
class Car
{
	public string Name { get; private set; }
    public Car(string name)
    {
    	if (string.IsNullOrEmpty(name))
        {
        	// 빈 값이면 강제로 ArgumentException 예외 발생
            throw ne ArgumentException();
        }
        this.Name = name;
    }
}
```

#### 38.15 메서드로 속성 값 초기화하기

```cs
class Fish
{
	public int Weight { get; set; } = 50;
    public void Feed(int weight) => Weight += weight;
    
    class PropertyPractice
    {
    	static void Main()
        {
        	var fish = new Fish();
            fish.Weight = 10;
            fish.Feed(15);
            Console.WriteLine(fish.Weight);
        }
    }
}
```

#### 38.16 속성에서 ?.와 ?? 연산자를 함께 사용하기

```cs

class Person
{
	public string Name { get; set; }
    public Address Address { get; set; }
}

class Adress
{
	public string Street { get; set; } = "알 수 없음";
}

(...)

WriteLine($"person?.Name ?? "아무개"}은(는)" + $"{person?.Address.Street ?? "아무곳"}에 삽니다.");

WriteLine($"첫 번째 사람 : {otherPeople?[0]?.Name ?? "없음"}");
```

Name 속성이 null이 아니면 해당 Name 속성 사용, 아니면 null 반환하여 ?? 연산자 통해 "아무개" 반환

otherPeople?\[0]?.Name은 \[0]번째 인덱스의 배열 값이 null인지 확인

### 39 인덱서와 반복기

#### 39.1 인덱서

```cs
public string this[int index]
{
	get { return (index % 2 == 0) ? "짝수" : "홀수"; }
}
```

인덱서는 클래스의 인스턴스를 배열처럼 사용할 수 있게 하는 구문이다. 쉽게 말해서, 인스턴스 이름 옆에 \[index] 붙이면 무슨 행동을 할지 정의하는 메서드이다.

아래의 코드를 보면 조금 더 이해가 잘 된다.

```cs
public class Person
{
    public string Name { get; set; }
    public string Gender { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }

    public Object this [int index]
    {
        get
        {
            if (index == 0)
                return Name;
            else if (index == 1)
                return Gender;
            else if (index == 2)
                return Age;
            else if (index == 3)
                return Email;
            else
                return null;
        }
        set
        {
            if (index == 0)
                Name = value.ToString();
            else if (index == 1)
                Gender = value.ToString();
            else if (index == 2)
                Age = Convert.ToInt32(value);
            else if (index == 3)
                Email = value.ToString();
        }
    }

    public override string ToString()
    {
        return "Name: " + Name + " / Gender: " + Gender + " / Age: " + Age + " / Email: " + Email;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new Person() {
            Name="Tom", 
            Gender="Male", 
            Age= 20, 
            Email="Tom@Test.com" 
        };

        Console.WriteLine("Name: " + person[0]);
        Console.WriteLine("Gender: " + person[1]);
        Console.WriteLine("Age: " + person[2]);
        Console.WriteLine("Email: " + person[3]);
    }
}

```

코드 출처: <https://developer-talk.tistory.com/323>

person\[0] 넣으면 Name를 반환하고, person\[1] 넣으면 Gender를 반환한다.

이렇게 멤버에 인덱싱을 해줄 수 있는게 바로 인덱서다. 숫자 말고 문자열로 인덱싱을 해줄 수도 있다.

#### 39.2 인덱서를 사용하여 배열 형식의 개체 만들기

```cs
public class Car
{
	private string[] names;
    public Car(int length) { names = new string[length]; }
    public string this[int index]
    {
    	get
        {
        	return names[index];
        }
        set
        {
        	names[index] = value;
        }
    }
}
```

이제 Car의 insatnce인 NewCar을 `NewCar[0]` 이런 식으로 사용하면 names 멤버를 인덱스대로 사용할 수 있다.

#### 39.4 반복기와 yield 키워드

반복기(iterator)는 배열과 컬렉션 형태의 데이터를 단계별로 실행하는 데 사용할 수 있다. 반복기를 구현할 때는 `IEnumerable` 인터페이스 (또는 `IEnumerable<T>` 인터페이스)와 `yield` 키워드를 사용한다.

```cs
class YieldReturn
{
    static IEnumerable MultiData()
    {
        yield return "Hello";
        yield return "World";
        yield return "C#";
    }

    static void Main()
    {
        foreach (var item in MultiData())
        {
            Console.WriteLine(item);
        }
    }
}
```

실행하면

```
Hello
World
C#
```

요청이 올 때마다 하나씩만 반환된다는 걸 알 수 있다.

**`IEnumerable<T>`로 컬렉션 형태의 데이터 반환받기**

```cs
> using System.Collections.Generic;
> 
> // 1. not using yield
> static IEnumerable<int> Greater1(int[] numbers, int greater)
. {
.     List<int> temp = new List<int>();
.     foreach (var n in numbers)
.     {
.         if (n > greater)
.         {
.             temp.Add(n);
.         }
.     }
.     return temp;
. }
> // 2. using yield
> static IEnumerable<int> Greater2(int[] numbers, int greater)
. { 
.     foreach(var n in numbers)
.     {
.         if (n > greater)
.         {
.             yield return n;
.         }
.     }
. }
>
> int[] numbers = { 1, 2, 3, 4, 5 };
>
> foreach (var n in Greater1(numbers, 3))
. {
.     Console.WriteLine(n);
. }
4
5
> foreach(var n in Greater2(numbers, 3))
. {
.     Console.WriteLine(n);
. }
4
5
```

yield return으로 반복해서 처리해야 하는 데이터를 반환할 때 유용하게 사용할 수 있다.

**`IEnumerable<T>`의 MoveNext() 메서드와 Current 속성**

`IEnumerable<T>` 형태는 내부적으로 MoveNext() 메서드와 Current 속성을 사용한다.

```cs
> IEnumerable<int> GetNumbers()
. {
.     yield return 1;
.     yield return 2;
.     yield return 5;
. }
> 
> var nums = GetNumbers().GetEnumerator();
> nums
GetNumbers { 1, 2, 5 }
> 
> nums.MoveNext();
> nums.Current
1
> nums.MoveNext()
true
> nums.Current
2
```

다음 값 이동, 반환하는 예제


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lazyartisan.gitbook.io/note/main-page/books/c/30-39.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
