일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- jupyter lab
- 구조체
- 플러터
- gitlab
- c#
- Flutter
- HTML
- jupyter
- Python
- c# 추상 클래스
- github
- docker
- 포인터
- 유니티
- Houdini
- git
- C언어 포인터
- Algorithm
- Unity
- c# 윈폼
- c언어
- 깃
- C# delegate
- dart 언어
- c# winform
- 도커
- vim
- C++
- 다트 언어
- Data Structure
- Today
- Total
목록Programming (313)
nomad-programmer
중첩 클래스(Nested Class)는 클래스 안에 선언되어 있는 클래스를 말한다. class OuterClass { class NestedClass { } } 중첩 클래스를 선언하는 문법은 매우 간단하다. 클래스 안에 클래스를 선언하는 것이 전부다. 객체를 생성하거나 객체의 메소드를 호출하는 방법도 보통의 클래스와 다르지 않다. 한 가지 중첩 클래스가 다른 클래스와 다른 점이 있다면, 자신이 소속되어 있는 클래스의 멤버에 자유롭게 접근할 수 있다는 사실이다. private 멤버에도 접근할 수 있다. 중첩 클래스를 사용하는 이유 클래스 외부에 공개하고 싶지 않은 형식을 만들고자 할 때 현재 클래스의 일부분처럼 표현할 수 있는 클래스를 만들고자 할 때 다른 클래스의 private 멤버에도 접근할 수 있는 ..
클래스를 상속이 안 되도록 봉인하는 것처럼 메소드도 오버라이딩되지 않도록 봉인할 수 있다. 그렇다고 모든 메소드를 봉인할 수 있는 것은 아니다. virtual로 선언된 가상 메소드를 오버라딩한 버전의 메소드만이 가능하다. using System; namespace CSharpExample { class Base { public virtual void SealMe() { } } class Derived : Base { public sealed override void SealMe() { base.SealMe(); } } class WantToOverride : Derived { public override void SealMe() { } } class MainApp { static int Main(stri..
메소드 숨기기란, CLR에게 부모 클래스에서 구현된 버전의 메소드를 감추고 파생 클래스에서 구현된 버전만을 보여주는 것을 말한다. 메소드 숨기기는 파생 클래스 버전의 메소드를 new 한정자로 수식함으로써 할 수 있다. (생성자를 호출할 때 사용하는 new 연산자와는 다르다) 메소드 숨기기는 오버라이딩과 다르다. 이름 그대로 메소드를 숨기고 있을 분이다. 메소드 숨기기는 완전한 다형성을 표현하지 못하는 한계를 갖고 있다. 따라서 부모 클래스를 설계할 때는 파생 클래스의 모습까지 고려해야 한다. using System; namespace CSharpExample { class Base { public void MyMethod() { Console.WriteLine("Base.MyMethod()"); } } ..
객체 지향 프로그래밍에서 다형성(Polymorphism)은 객체가 여러 형태를 가질 수 있음을 의미한다. 다형성은 원래 하위 형식 다형성(Subtype Polymorphism)의 준말이다. 다시 말해, 자신으로부터 상속받아 만들어진 파생 클래스를 통해 다형성을 실현하는 것이다. 메소드를 오버라이딩하기 위해서는 한 가지 조건이 필요하다. 그 조건은 바로 오버라이딩을 할 메소드가 virtual 키워드로 한정되어 있어야 한다는 것이다. private로 선언한 메소드는 오버라이딩할 수 없다. private로 선언된 멤버는 어차피 파생 클래스에서 보이지도 않는다. 같은 이름, 같은 형식, 같은 매개 변수를 이용하여 선언했다 하더라도 컴파일러는 메소드를 재정의한다고 생각하지 않고 전혀 없었던 메소드를 선언했다고 간..
C#은 C++의 순수 가상 함수같은 말이 없다. 허나 순수 가상 함수와 같은 기능을 하는 것을 만들 수 있다. 그것은 추상 클래스를 정의하고 abstract 키워드를 사용하는 것이다. 추상 클래스와 가상 함수의 차이점 가상 함수는 재정의를 하지 않아도 컴파일 에러가 발생하지 않는다. 또한 부모 클래스에서 가상 함수는 {} 괄호가 있어야 한다. 즉, 함수 같은 구조여야 한다. public virtual string GetData() { return ""; } 추상 클래스의 추상화 함수는 재정의를 하지 않으면 컴파일 에러가 발생한다. 그리고 부모 클래스에서 {} 괄호는 필요치 않다. 선언만 존재하면 된다. public abstract string GetData(); 추상 클래스는 독자적으로 인스턴스를 생성할..
개와 고양이는 종은 다르지만 젖을 먹인다는 공통점이 있다. 그러기에 포유류로 분류된다. 이를 플래스로 다음과 같이 표현할 수 있다. class Mammal { public void Nurse() {} } class Dog : Mammal { public void Bark() {} } class Cat : Mammal { public void Meow() {} } // ///////////////////////// Mammal mammal = new Dog(); mammal.Nurse(); Dog dog = (Dog)mammal; mammal = new Cat(); mammal.Nurse(); 위의 예처럼 부모 클래스와 파생 클래스 사이를 왔다 갔다 형식 변환이 가능하다. 파생 클래스의 인스턴스는 부모 클..
class 부모 클래스 { // 멤버 선언 } class 파생 클래스 : 부모 클래스 { // 아무 멤버를 선언하지 않아도 부모 클래스의 모든 것을 물려받아 갖게 된다. // 단, private로 선언된 멤버는 예외이다. } 파생 클래스의 이름 뒤에 콜론(:)을 붙여주고 그 뒤에 상속받을 부모 클래스의 이름을 붙여주면 된다. 만약 부모 클래스의 생성자가 매개 변수를 입력받도록 선언되어 있다면 파생 클래스의 인스턴스를 생성할 때 호출되는 부모 클래스의 생성자에게 어떻게 매개 변수를 전달해줄 수 있을까? 이럴 때는 base 키워드를 사용하면 된다. this 키워드가 "자기 자신"을 가리킨다면 base는 "부모 클래스"를 가리킨다. this를 통해 자기 자신의 멤버에 접근할 수 있었던 것처럼, base 키워드..
객체 지향 프로그래밍에서 필요한 최소의 기능만을 노출하고 내부를 감출 것을 요구한다. 이것을 은닉성이라고 한다. 객체 지향 프로그래밍의 3대 특성 객체 지향 프로그래밍에는 여러 가지 특성이 있다. 그 중에서도 3대 특성으로 꼽히는 것들이 있다. 이 셋중의 하나가 바로 은닉성이며 나머지 2개는 상속성(Inheritance)과 다형성(Polymorphism)이다. 클래스에 선언되어 있는 필드와 메소드 중에 어떤 것들은 사용자에게 노출할 것이 있는가 하면, 절대로 노출시키지 말아야 하는 것들도 있다. 특히 필드는 상수를 제외하곤 "무조건" 감추는 것이 좋다. 접근 한정자 (Access Modifier)는 감추고 싶은 것은 감추고, 보여주고 싶은 것은 보여줄 수 있도록 코드를 수식하며, 필드, 메소드를 비롯해 ..
class MyClass { int a, b, c; public MyClass() { this.a = 555; } public MyClass(int b) { this.a = 555; this.b = b; } public MyClass(int b, int c) { this.b = b; this.c = c; } } 위의 코드는 문제가 있는 코드인가? 그렇지 않다. 문법적으로도 문제가 없고 동작도 잘 될 것이다. 다만 MyClass() 생성자 안에 똑같은 코드가 중복되어 들어가 있다. 비 생성적이다. 이런 고민을 해결해주는 것이 this() 이다. this가 객체 자신을 지칭하는 키워드인 것처럼, this() 는 자기 자신의 생성자를 가리킨다. this() 는 생성자에서만 사용될 수 있다. 그것도 생성자의 코..
데이터 형식에서 참조 형식과 값 형식이 무엇인지 정확히 알아야 한다. 참조 형식은 힙 영역에 객체를 할당하고 스택에 있는 참조가 힙 영역에 할당된 메모리를 가리킨다. 클래스는 태생이 참조 형식이다. 그래서 객체를 복사할 때 주의해야 한다. 무작정 대입 연산으로 복사되었다고 생각하면 안된다. 왜냐면 참조 형식이기 때문에 변수는 힙 영역에 할당된 객체의 주소 값만을 지니고 있다. 이것을 다른 변수에 대입하면 해당 주소만 넘겨주는 꼴이된다. 그럼 그 다른 변수도 힙 영역의 주소를 가리키게되므로 서로 공유된 메모리를 갖게된다. 이렇게 객체를 복사할 때 참조만 살짝 복사하는 것을 "얕은 복사 (shallow copy)"라고 한다. 원본으로부터 별도의 힙 공간에 객체를 복사하는 것은 "깊은 복사 (deep copy..
객체는 생성자에 의해 만들어지고 소멸자에 의해 사라진다. 생성자 생성자는 클래스와 이름이 같고, 반환 형식이 없다. 생성자의 임무는 단 한 가지, 해당 클래스의 객체를 생성하는 것뿐이다. 그러기에 반환할 것이 없다. 클래스를 선언할 때 명시적으로 생성자를 구현하지 않아도 컴파일러에서 생성자를 만들어준다 (이런 생성자를 기본 생성자라 한다). 객체를 다루다 보면 객체를 생성하는 시점에 객체의 필드를 원하는 값으로 초기화하고 싶을 때가 이런 작업을 할 수 있는 최적의 장소가 바로 객체를 생성하기 위해 호출하는 메소드인 생성자이다. 생성자도 오버로딩이 가능하다 소멸자 종료자의 이름은 클래스 이름 앞에 ~를 붙인다. 그리고 소멸자는 생성자와는 달리 매개 변수도 없고, 한정자도 사용하지 않는다. 또한 여러 버전의..
객체를 생성할 때 다음과 같이 코드를 작성한다. Cat kitty = new Cat(); 위 문장의 가장 끝에 있는 Cat()는 생성자(Constructor) 라고 하는 특별한 메소드이다. 생성자는 클래스의 이름과 동일한 이름을 가지며, 객체를 생성하는 역할을 한다. Cat() 생성자 앞에 있는 new 키워드는 생성자를 호출해서 객체를 생성하는 데 사용하는 연산자이다. 말하자면 new 연산자와 생성자는 바늘과 실 같은 존재라고 할 수 있다. heap 메모리 공간에 객체를 생성한다. 그러니 당연하게도 할당시키는 연산자 new를 써야 한다. C++ 언어에서 new, C언어에서 malloc과 같다고 생각하면 된다. Cat 클래스를 비롯한 모든 클래스는 복합 데이터 형식이다. 그리고 복합 데이터 형식은 참조 형..
로컬 함수 (Local Function)는 메소드 안에서 선언되고, 선언된 메소드 안에서만 사용되는 특별한 함수이다. 클래스의 멤버가 아니기 때문에 메소드가 아니라 함수 (Function)라고 부른다. 선언 방법은 메소드와 다르지 않지만, 로컬 함수는 자신이 존재하는 지역에 선언되어 있는 변수를 사용할 수 있다. 로컬 함수는 메소드 밖에서는 다시 쓸 일 없는 반복적인 작업을 하나의 이름 아래 묶어 놓는 데 제격이다. 람다식과 더불어 프로그래머에게 코드를 간추릴 수 있는 또 하나의 옵션을 제공하는 것이다. using System; namespace CSharpExample { class MainApp { static string ToLowerString(string input) { // arr -> cha..
메소드의 매개 변수는 기본 값을 가질 수 있다. 기본 값을 가지는 매개 변수는 필요에 따라 데이터를 할당하거나 할당하지 않을 수 있기 때문에 이를 "선택적 매개 변수 (Optional Parameter)" 라고 부른다. 선택적 매개 변수는 메소드의 사용자에게 사용하지 않는 매개 변수를 염두에 두지 않도록 편의를 제공하지만, 또 한편으로는 모호함이라는 스트레스도 같이 준다. 코드를 작성할 때는 자동 완성의 도움을 받아 어느 매개 변수에 데이터를 할당하는지 파악할 수 있지만, 작성한 지 한참이 지난 코드를 다시 들려다 보면 어느 코드에 데이터를 할당했는지 분간이 잘 안될 때도 있다. 매개 변수의 수가 많고 여기에 선택적 매개 변수도 여럿 포함되면 더 헛갈려진다. 이런 경우 명명된 매개 변수의 도움을 받으면 ..
메소드를 호출할 때 매개 변수 목록 중 어느 매개 변수에 데이터를 할당할 것인지를 지정하는 것은 "순서"이다. 대개의 경우 순서에 근거해서 매개 변수에 데이터를 할당하는 스타일을 사용하지만, C#은 또 다른 스타일도 지원한다. 물론 C#뿐 아니라 다른 언어에서도 지원한다. 명명된 매개 변수 (Named Parameter) 말 그대로 메소드를 호출할 때 매개 변수의 이름에 근거해서 데이터를 할당하는 기능이다. 명명된 매개 변수를 사용하기 위해 메소드 선언에 손댈 일은 전혀 없다. 메소드를 호출할 때만 매개 변수의 이름 뒤에 콜론(:)을 붙인 뒤 그 뒤에 할당할 데이터를 넣어주면 된다. using System; namespace BrainCSharp { class HelloWorld { static void..
코딩을 하다 보면 똑같은 그저 매개 변수의 "수"가 다르다는 이유만으로 똑같은 메소드를 여러 가지 버전으로 오버로딩하고 싶을 때가 있다. 이런 경우를 위해 C#은 "가변 길이 매개 변수"라는 기능을 제공한다. 가변 길이 매개 변수란, 그 개수가 유연하게 변할 수 있는 매개 변수를 말한다. 이것을 이용하면 다음과 같이 입력되는 모든 매개 변수의 합을 구하는 Sum() 메소드를 오버로딩하지 않고도 구현할 수 있다. int total = 0; total = Sum(1, 2); total = Sum(1, 2, 3); total = Sum(1, 2, 3, 4, 5, ...); 가변 길이 매개 변수는 params 키워드와 배열을 이용해서 선언한다. 매개 변수의 개수가 유연하게 정해져 있다면 가변 길이 매개 변수보다..
대개의 경우 메소드의 결과는 하나면 충분하다. 허나 두 개 이상의 결과를 요구하는 특별한 메소드도 있다. 이를테면 나눗셈을 구현할 때 제수와 피제수를 매개 변수로 넘겨받고 결과는 몫과 나머지 두 개로 반환할 필요가 있다. 이런 경우, ref 키워드를 이용해 메소드를 구현하면 몫과 나머지를 한 번에 반환할 수 있다. ref만으로도 여러 개의 결과를 메소드로부터 얻어올 수 있지만 C#은 조금 더 "안전한 방법"으로 똑같은 일을 할 수 있게 해준다. 바로 "out" 키워드를 이용한 "출력 전용 매개 변수"가 그것이다. out 키워드의 사용법은 간단하다. 메소드의 선언부와 호출부에 ref키워드 대신 out 키워드를 사용하는 것이 전부이다. 하지만 out은 ref에게는 없는 "안전 장치"가 있다. ref 키워드를..
C#에서 참조에 의한 매개 변수 전달은 아주 쉽다. ref 키워드만 입력해주면 된다. 물론 C++도 쉽지만... C#의 ref 키워드는 C++의 &(참조자)와 똑같은 개념이다. 매개 변수를 메소드에 참조로 전달 using System; namespace SwapRef { class MainApp { static void Swap(ref int a, ref int b) { int temp = b; b = a; a = temp; } static void Main(string[] args) { int x = 3; int y = 4; Console.WriteLine($"x:{x}, y:{y}"); Swap(ref x, ref y); Console.WriteLine($"x:{x}, y:{y}"); } } } /*..
goto 레이블; 레이블: // 코드 goto 문은 레이블이 가리키는 곳으로 바로 뛰어 넘어간다. 상당수의 프로그래머들은 goto문을 별로 좋아하지 않는다. goto문이 코드의 흐름을 자주 끊어 읽기 어렵게 만들기 때문이다. 컴퓨터 과학 학계에서는 "goto의 해악"을 주제로 하는 논문도 여러 편 발표된적이 있다. 그럼에도 불구하고 goto문도 유용한 경우가 있는데. 다음 코드처럼 대표적인 예가 중첩된 반복문을 단번에 뚫고 나오기 위해 사용하는 것이다. using System; namespace CSharpExam { class GotoExample { static int Main(string[] args) { Console.Write("종료 조건 입력: "); string input = Console...
정수와 문자열 형식 외에도, C# 7.0부터 switch문에 데이터 형식을 조건으로 사용할 수 있게 되었다. 데이터 형식에 따라 분기할 때에는 case 절에서 데이터 형식 옆에 반드시 식별자를 붙여줘야 한다. 선언한 식별자는 case 절 안에서 사용할 수 있다. 또한 when 절을 이용하여 추가적인 조건 검사를 수행할 수 있다. when은 case 절에 붙여 사용한다. when 절을 if 문과 비슷하다고 생각하면 된다. using System; namespace CSharpExam { class SwitchExample { static int Main(string[] args) { object obj = null; string s = Console.ReadLine(); if (int.TryParse(s,..