구조체
struct MyStruct
{
public int MyField1
public int MyField2
public void MyMethod()
{
...
}
}
** 구조체는 데이터를 담기 위한 자료구조로 사용됩니다. 따라서 굳이 은닉성을 비롯한 객체지향의 원칙을 구조체에 강하게 적용하지 않는 편이며, 편의를 위해 필드를 public으로 선언해서 사용하는 경우가 많습니다.
특징 | 클래스 | 구조체 |
키워드 | class | struct |
형식 | 참조 형식(힙에 할당) | 값 형식(스택에 할당) |
복사 | 얕은 복사(Shallow Copy) | 깊은 복사(Deep Copy) |
인스턴스 생성 | new 연산자와 생성자 필요 | 선언만으로 생성 |
생성자 | 매개변수 없는 생성자 선언 가능 | 매개변수 없는 생성자 선언 불가능 |
상속 | 가능 | 값 형식이므로 상속 불가능 |
using System;
namespace Structure
{
struct Point3D
{
public int X;
public int Y;
public int Z;
public Point3D(int X, int Y, int Z)
{
this.X = X;
this.Y = Y;
this.Z = Z;
}
public override string ToString()
// System.Object형식의 ToString() 메소드를 오버라이딩합니다.
{
return string.Format($"{X}, {Y}, {Z}");
}
}
class MainApp
{
static void Main(string[] args)
{
Point3D p3d1; // 선언만으로도 인스턴스가 생성됩니다.
p3d1.X = 10;
p3d1.Y = 20;
p3d1.Z = 40;
Console.WriteLine(p3d1.ToString());
Point3D p3d2 = new Point3D(100, 200, 300); // 물론 생성자를 이용한 인스턴스 생성도 가능합니다.
Point3D p3d3 = p3d2; // 구조체의 인스턴스를 다른 인스턴스에 할당하면 깊은 복사가 이루어집니다.
p3d3.Z = 400;
Console.WriteLine(p3d2.ToString());
Console.WriteLine(p3d3.ToString());
}
}
}
튜플
튜플도 여러 필드를 담을 수 있는 구조체입니다. 하지만 구조체와는 달리 튜플은 형식 이름이 없습니다.
그래서 튜플은 응용 프로그램 전체에서 사용할 형식을 선언할 때가 아닌, 즉석에서 사용할 복합 데이터 형식을 선언할 때 적합합니다. 튜플을 구조체이므로 값 형식입니다. 값형식은 생성된 지역을 벗어나면 스택에서 소멸되기 때문에 프로그램에 장기적인 부담을 주지 않는 장점이 있습니다.
** C# 튜플(Tuple)은 여러 데이터 요소를 하나의 간단한 데이터 구조로 그룹화할 수 있는 기능입니다.
[장점]
1. 여러 값을 간단히 그룹화할 수 있습니다.
2. 메서드에서 여러 값을 쉽게 반환할 수 있습니다.
3. 코드의 가독성과 간결성을 향상합니다.
- 여러 값을 반환해야 하는 메서드에서 유용하게 사용됩니다. 또한 데이터베이스 쿼리 결과 저장, 오브젝트 속성저장 등 다양한 실제 응용분양에서 활용될 수 있습니다.
using System;
namespace Tuple
{
class MainApp
{
static void Main(string[] args)
{
//명명되지 않은 튜플
var a = ( "슈퍼맨", 9999 );
Console.WriteLine($"{a.Item1}, {a.Item2}");
//명명된 튜플
var b = (Name: "박상현", Age: 17);
Console.WriteLine($"{b.Name}, {b.Age}");
//분해
var (name, age) = b; //(var name, var age) = b;
Console.WriteLine($"{name}, {age}");
//분해2
var (name2, age2) = ("박문수", 34);
Console.WriteLine($"{name2}, {age2}");
//명명된 튜플 = 명명되지 않은 튜플
b = a;
Console.WriteLine($"{b.Name}, {b.Age}");
}
}
}
튜플 분해
1. 여러값을 한 번에 할당할 수 있어 코드가 간결해진다.
2. 메서드에서 여러 값을 반환할 때 유용하다.
3. 임시 변수 사용을 줄여 코드 가독성을 높인다.
using System;
class Decomposition
{
static void Main(string[] args)
{
// 튜플 분해 사용
var (name, age) = GetUserInfo();
Console.WriteLine($"이름: {name}, 나이: {age}");
// 프로그램이 즉시 종료되지 않도록 사용자 입력 대기
Console.WriteLine("프로그램을 종료하려면 아무 키나 누르세요...");
Console.ReadKey();
}
static (string Name, int Age) GetUserInfo()
{
return ("Alice", 30);
}
}
인터페이스와 추상 클래스
인터페이스의 선언은 메서드, 이벤트, 인덱서, 프로퍼티만을 가질 수 있습니다.
interface ILogger
{
void WriteLog(string message);
}
** 인터페이스는 접근제한 한정자를 사용할 수 없고 모든 것이 Public으로 선언됩니다.
** 인터페이스를 상속받는 클래스의 인스턴스를 만드는 것은 가능합니다.
파생 클래스는 인터페이스에 선언된 모든 메서드(및 프로퍼티)를 구현해줘야 하며, 이 메서드들은 public 한정자로 수식해야 합니다.
class ConsoleLogger : ILLoger
{
public void WriteLog(string message)
{
Console.WriteLine( "{0} {1}", DateTiem.Now.ToLocalTime(), message);
}
}
// 인스턴스 화
ILogger logger = new ConsoleLogger();
logger.WriteLog("Hello, World!");
** 인터페에스는 인스턴스를 못 만들지만, 참조는 만들 수 있습니다. 이 참조에 파생 클래스의 객체의 위치를 담는 것입니다.
즉, ConsoleLogger의 객체는 ILogger의 객체로 취급할 수 있다
인터페이스 작명법 : 이름 앞에 I를 붙여 사용
using System;
using System.IO;
namespace Interface
{
interface ILogger
{
void WriteLog(string message);
}
class ConsoleLogger : ILogger
{
public void WriteLog(string message)
{
Console.WriteLine("{0} {1}", DateTime.Now.ToLocalTime(), message);
}
}
class FileLogger : ILogger
{
private StreamWriter writer;
public FileLogger(string path)
{
writer = File.CreateText(path);
writer.AutoFlush = true;
}
public void WriteLog(string message)
{
writer.WriteLine("{0} {1}", DateTime.Now.ToShortTimeString(), message);
}
}
class ClimateMonitor
{
private ILogger logger;
public ClimateMonitor(ILogger logger)
{
this.logger = logger;
}
public void start()
{
while(true)
{
Console.Write("온도를 입력해주세요: ");
string temperature = Console.ReadLine();
if(temperature == "")
{
break;
}
logger.WriteLog("현재 온도: " + temperature);
}
}
}
class MainApp
{
static void Main(string[] args)
{
ClimateMonitor monitor = new ClimateMonitor(new FileLogger("MyLog.txt"));
monitor.start();
}
}
}
인터페이스는 USB와 같은 역할을 한다.
인터페이스로부터 파생될 클래스가 어떤 메서드를 구현해야 할지를 정의한다.
앞에서 예로 들었던 ILogger 인터페이스는 자신으로부터 파생될 클래스가 반드시 WriteLog()메서드를 구현하도록 강제합니다. 어떤 클래스든 ILogger를 상속받아 ILogger에 선언되어 있는 WriteLog() 메소드를 구현하면 ILogger의 역할을 할 수 있습니다. 앞에서 ILogger인터페이스를 상속받는 ConsoleLogger 클래스를 선언하여 콘솔에 로그를 출력하도록 WriteLog() 메서드를 구현했는데, 이외에도 얼마든지 ILogger를 상속받는 새로운 클래스를 선언해서 파일에 로그를 출력하도록 할 수 있고 네트워크 너머에 있는 서버에 저장하도록 패킷을 보낼 수도 있습니다.
예를 들어 사용자로부터 온도를 반복적으로 입력받아 기록하는 ClimateMonitor클래스를 만드는데, 로그를 저장하는 방식 등은 ClimateMonitor 클래스를 사용하는 개발자에 따라 결정할 수 있다. ClimateMonitor 클래스는! Logger 형식의 참조 logger를 이용해서 사용 자로부터 입력받은 온도를 기록합니다. logger가 어떻게 이 메시지를 기록할지는 ClimateMonitor() 생성자의 매개변수에 입력된 객체에 달려 있습니다.
인터페이스를 상속하는 인터페이스
인터페이스를 상속할 수 있는 것은 클래스뿐이 아닙니다. 클래스의 사촌인 구조체는 물론이고, 인터 페이스도 인터페이스를 상속할 수 있습니다. 기존 인터페이스에 새로운 기능을 추가한 인터페이스를 만들고 싶을 때 인터페이스를 상속하는 인터페이스를 만들면 됩니다.
인터페이스가 인터페이스를 상속하기 위해 사용하는· 문법은 클래스의 문법과 똑같습니다. 파생 인 터페이스의 이름 옆에 ‘콜론(:)’을 입력하고 그 오른편에 상속할 인터페이스의 이름을 붙여주면 됩니다.
interface 파생_인터페이스 : 부모_인터페이스
{
// 메서드 목록
}
** 파생 인터페이스는 기반 인터페이스에 선언된 모든 것을 그대로 물려받습니다.
interface ILogger
{
void WriteLog(string message);
}
ILogger 인터페이스를 상속하는 새로운 인터페이스를 만든다면 다음 IFormattableLogger 인터페이스는 ILogger에 선언된 void WriteLog(string message)와 void WriteLog(string format, params Object [] args) 메서드를 두 개를 갖게 됩니다.
interface IFormattableLogger : ILogger
{
void WriteLog(string format, params Object[] args);
}
using System;
namespace DerivedInterface
{
interface ILogger
{
void WriteLog(string message); // 문자열 메시지를 받아 로그를 기록하는 WriteLog 메서드를 선언합니다.
}
interface IFormattableLogger : ILogger // ILogger 인터페이스를 상속하는 IFormattableLogger 인터페이스를 정의합니다.
{
void WriteLog(string format, params Object[] args); // 형식 문자열과 객체 배열을 받아 로그를 기록하는 WriteLog 메서드를 선언합니다.
}
class ConsoleLogger2 : IFormattableLogger // IFormattableLogger 인터페이스를 구현하는 ConsoleLogger2 클래스를 정의합니다.
{
public void WriteLog(string message) // ILogger 인터페이스의 WriteLog 메서드를 구현합니다.
{
Console.WriteLine("{0} {1}", DateTime.Now.ToLocalTime(), message); // 현재 시간과 메시지를 콘솔에 출력합니다.
}
public void WriteLog(string format, params Object[] args) // IFormattableLogger 인터페이스의 WriteLog 메서드를 구현합니다.
{
String message = String.Format(format, args); // 형식 문자열과 객체 배열을 사용하여 메시지를 생성합니다.
Console.WriteLine("{0} {1}", DateTime.Now.ToLocalTime(), message); // 현재 시간과 메시지를 콘솔에 출력합니다.
}
}
class MainApp
{
static void Main(string[] args)
{
IFormattableLogger logger = new ConsoleLogger2(); // ConsoleLogger2 객체를 생성하고 IFormattableLogger 인터페이스 타입으로 변수에 할당합니다.
logger.WriteLog("The world is not flas."); // WriteLog 메서드를 호출하여 문자열 메시지를 출력합니다.
logger.WriteLog("{0} + {1} = {2}", 1, 1, 2); // WriteLog 메서드를 호출하여 형식 문자열과 숫자를 사용하여 메시지를 출력합니다.
}
}
}
=> ILogger 인터페이스는 WriteLog 메서드를 선언하여 로그를 기록하는 기본적인 기능을 정의합니다.
=> IFormattableLogger 인터페이스는 ILogger 인터페이스를 상속하고, 형식 문자열과 인자를 사용하여 로그를 기록하는 추가적인 기능을 제공합니다.
=> MainApp 클래스는 ConsoleLogger2 객체를 생성하고 WriteLog 메서드를 호출하여 로그를 기록하는 방법을 보여줍니다.
=> params 키워드는 가변 개수의 인자를 메서드에 전달할 수 있도록 합니다.
=> String.Format 메서드는 형식 문자열과 인자를 사용하여 문자열을 생성합니다.
다중 상속 인터페이스
using System;
namespace MultiInterfaceInheritance
{
interface IRunnable // IRunnable이라는 인터페이스를 정의합니다.
{
void Run(); // Run() 메서드를 선언합니다. (자동차가 달리는 기능을 나타냅니다.)
}
interface IFlyable // IFlyable이라는 인터페이스를 정의합니다.
{
void Fly(); // Fly() 메서드를 선언합니다. (자동차가 나는 기능을 나타냅니다.)
}
class FlyingCar : IRunnable, IFlyable // FlyingCar 클래스는 IRunnable 인터페이스와 IFlyable 인터페이스를 모두 상속합니다.
{
public void Run() // IRunnable 인터페이스의 Run() 메서드를 구현합니다.
{
Console.WriteLine("Run! Run!"); // 콘솔에 "Run! Run!"을 출력합니다.
}
public void Fly() // IFlyable 인터페이스의 Fly() 메서드를 구현합니다.
{
Console.WriteLine("Fly! Fly!"); // 콘솔에 "Fly! Fly!"를 출력합니다.
}
}
class MainApp // MainApp 클래스를 정의합니다.
{
static void Main(string[] args) // 프로그램의 진입점인 Main 메서드를 정의합니다.
{
FlyingCar car = new FlyingCar(); // FlyingCar 클래스의 객체 car를 생성합니다.
car.Run(); // car 객체의 Run() 메서드를 호출합니다.
car.Fly(); // car 객체의 Fly() 메서드를 호출합니다.
IRunnable runnable = car as IRunnable; // car 객체를 IRunnable 인터페이스 타입으로 변환합니다.
runnable.Run(); // runnable 객체의 Run() 메서드를 호출합니다.
IFlyable flyable = car as IFlyable; // car 객체를 IFlyable 인터페이스 타입으로 변환합니다.
flyable.Fly(); // flyable 객체의 Fly() 메서드를 호출합니다.
}
}
}
=> FlyingCar 클래스는 IRunnable 인터페이스와 IFlyable 인터페이스를 모두 상속하여 자동차가 달리고 나는 기능을 모두 구현합니다.
=> MainApp 클래스에서는 FlyingCar 객체를 생성하고 Run() 메서드와 Fly() 메서드를 호출하여 자동차가 달리고 나는 기능을 실행합니다.
=> 또한, FlyingCar 객체를 IRunnable 인터페이스 타입과 IFlyable 인터페이스 타입으로 변환하여 각 인터페이스의 메서드를 호출하는 방법을 보여줍니다.
- 인터페이스: 특정 기능을 구현하도록 강제하는 계약입니다. 클래스는 여러 개의 인터페이스를 상속할 수 있습니다.
- 다중 인터페이스 상속: 하나의 클래스가 여러 개의 인터페이스를 상속하는 것을 의미합니다. 이를 통해 클래스는 다양한 기능을 구현할 수 있습니다.
- as 연산자: 객체를 특정 타입으로 변환합니다. 변환할 수 없는 경우 null을 반환합니다.
마치 사람이 운전면허와 조종사 면허를 모두 가지고 있는 것과 같습니다. 운전면허는 자동차를 운전할 수 있는 자격을, 조종사 면허는 비행기를 조종할 수 있는 자격을 나타냅니다. FlyingCar는 두 가지 면허를 모두 가지고 있는 사람처럼, 달리고 나는 두 가지 기능을 모두 가지고 있습니다.
추상 클래스 : 인터페이스와 클래스 사이
추상 클래스는 ‘구현’을 가질 수 있습니다. 하지만 클래스와 달리 인스턴스를 가질 수는 없습니다. 한 마디로, 추상 클래스는 구현을 갖되 인스턴스는 만들지 못합니다.
** 추상 클래스는 직접 객체를 생성할 수 없는 클래스
[특징]
- abstract 키워드: 추상 클래스는 abstract 키워드를 사용하여 선언합니다.
- 객체 생성 불가: 추상 클래스는 직접 객체를 생성할 수 없습니다.
- 상속: 다른 클래스가 추상 클래스를 상속받아서 사용할 수 있습니다.
- 추상 메서드: 추상 클래스는 구현되지 않은 메서드인 추상 메서드를 포함할 수 있습니다. 추상 메서드는 상속받는 클래스에서 반드시 구현해야 합니다.
- 일반 메서드: 추상 클래스는 일반 메서드도 포함할 수 있습니다.
[사용하는 이유]
- 공통적인 특징: 여러 클래스에서 공통적으로 사용하는 특징들을 추상 클래스에 정의하여 코드의 중복을 줄일 수 있습니다.
- 일관성: 추상 클래스를 사용하면 상속받는 클래스들이 특정 메서드를 반드시 구현하도록 강제하여 일관성을 유지할 수 있습니다.
- 유연성: 추상 클래스를 사용하면 프로그램의 구조를 유연하게 만들 수 있습니다.
abstract class Animal // Animal이라는 추상 클래스를 선언합니다.
{
public abstract void MakeSound(); // 추상 메서드 MakeSound()를 선언합니다.
public void Eat() // 일반 메서드 Eat()를 정의합니다.
{
Console.WriteLine("냠냠");
}
}
class Dog : Animal // Animal 클래스를 상속받는 Dog 클래스를 정의합니다.
{
public override void MakeSound() // 추상 메서드 MakeSound()를 구현합니다.
{
Console.WriteLine("멍멍!");
}
}
class Cat : Animal // Animal 클래스를 상속받는 Cat 클래스를 정의합니다.
{
public override void MakeSound() // 추상 메서드 MakeSound()를 구현합니다.
{
Console.WriteLine("야옹!");
}
}
=> Animal은 추상 클래스이고, MakeSound()는 추상 메서드입니다. Dog 클래스와 Cat 클래스는 Animal 클래스를 상속받아 MakeSound() 메서드를 각자의 방식으로 구현합니다.
'LAB > C#' 카테고리의 다른 글
C# ) C# 기초 (스레드 , 태스크 ,네트워크 프로그래밍) (0) | 2024.11.02 |
---|---|
C# ) C# 기초 5 (대리자, 이벤트) (0) | 2024.11.01 |
C# ) C# 기초 4 (일반화 프로그래밍, 예외처리) (0) | 2024.11.01 |
C# ) C# 기초 3 (프로퍼티, 컬렉션, 인덱서) (0) | 2024.10.31 |
C#) C# 기초 (데이터 형식 , 자료구조, 클래스) (0) | 2024.10.30 |