: 첫 번째 값에 의한 호출 (call by value) , 두 번째로 참조에 의한 호출 (call by reference)
📝call by value
값에 의한 호출
함수에 변수를 전달할 때 값을 복사해서 선달함
함수 내부에서 값을 변경해도 원래의 변수에 영향을 미치지 않는다
int, float, string, bool 등의 원시 타입은 값 복사
원시타입을 매개변수로 가지는 예시를 보겠다
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
int a = 10;
int b = 11;
CallbyValue(a, b);
}
public static void CallbyValue(int tempA, int tempB)
{
tempA++;
tempB++;
}
□함수에서 매개변수로 들어온 int값이 바뀌었지만, 원래 값은 변경되지 않는다.
□main함수가 실행될 때 스택 메모리에 a와 b 변수가 선언된다
□ CallByValue 메서드가 호출되면 스택에 tempA와 tempB가 할당되고 , a와 b의 값을 복사한다
□ 함수 실행이 종료되면 tempA와 tempB는 스택에서 해제된다
□ 즉, 함수 내에서 복사한 값을 변경하였기 때문에 원본 값은 변경되지 않는다
📝call by reference
참조에 의한 호출
함수에 변수를 전달할 때 값을 참조, 즉 메모리 주소를 전달
원래의 변수는 함수 외부에서도 변경된 값을 유지한다
객체, 배열 같은 참조타입은 참조값(주소)이 복사되어 함수에 전달
클래스를 참조값으로 가지는 예시를 보겠다
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Apple test = new Apple();
CallbyReference(test);
}
public static void CallbyReference(Apple test)
{
test.count += 10;
}
public class Apple
{
public int count = 1;
}
□함수 내에서 Apple클래스의 count값이 변경되었고, 원래 객체에도 변경이 반영된다
□ Apple클래스를 인스턴스와 하면 힙 메모리에 할당된다
□ apple 변수는 Apple 인스턴스의 찹조값(주소)을 가진다.
□ CallbyReference 메서드에 참조값(주소)을 전달하면, 해당 주소값이 복사되어 스택에 저장된다.
□함수 내에서는 참조값(주소)에 해당하는 객체에 접근하여 값을 변경시킨다
□ 즉, 참조된 객체의 속성을 변경하였기 때문에 원래 객체에도 변경이 반영된다
📝공통점 / 차이점
□ 공통점
모두 매개변수로 전달된 값 (값 자체 또는 참조값)을 복사한다
복사된 값은 스택에 저장되고 함수 종료 시 해제된다 (스택프레임과 연관 있음)
□ 차이점
Call by Value : 원시타입 (int 등)의 값 자체가 복사된다
Call by Reference : 객체의 참조 주소값이 복사된다
(+) 스택프레임
함수가 호출될 때, 스택 영역에 차례대로 저장되는 함수의 호출정보이다.
함수가 호출될 때, 매개변수를 스택에 할당하기 때문에 두 방식 다 매개변수로 전달된 값을 복사한다.
static 키워드를 사용하는 객체 처음 사용될 때 한번 할당되고, 프로그램이 끝날 때까지 메모리를 유지한다. 사용할 때 같은 메모리를 참조한다. 즉 정적이다.
반대로 동적인 객체는 사용할 때 마다 메모리를 사용하고, 사용이 끝나면 메모리를 해제한다.
📝 정적 변수
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
// 정적변수
// 클래스명.변수 < 로 접근
Cinema.person += 1;
Cinema cinema = new Cinema();
// 오류 : 인스턴스화 하면 정적 변수가 포함되지 않는다
// 정적 멤버는 클래스에 속하는 것이지 인스턴스에 속하는 것이 아님
cinema.person
}
public class Cinema
{
// 정적 변수
public static int person = 0;
public Cinema() { }
}
□ 정적으로 선언된 변수는 클래스명.변수명 으로 접근할 수 있다.
□ 정적 변수가 포함된 클래스를 인스턴스화 하면 정적 변수는 포함되지 않는다.
📝 정적 클래스
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
// 정적 클래스
Animal.animalAge += 1;
Animal.Setting();
}
public static class Animal
{
// 오류 : 정적 클래스에 인스턴스 변수를 선언할 수 없다
public int age = 0;
// 오류 : 정적 클래스에 인스턴스 메서드를 선언할 수 없다
public void Func() { }
// 정적 변수
public static int animalAge = 5;
// 정적 메서드
public static void Setting() { }
}
□ 정적으로 선언한 클래스의 필드와 메서드는 클래스명.변수명/ 클래스명. 함수명으로 접근할 수 있다.
🔖 정적선언을 할 때 주의해야할 점
1. 정적 클래스는 인스턴스화 할 수 없다.
2. 정적인 클래스는 정적인 변수와 메서드만 가질 수 있다.
📝 정적 메서드
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
// 정적 메서드
Food.Eat();
Food temp = new Food();
// 오류 : 인스턴스화 하면 정적 메서드는 포함되지 않는다
temp.Eat()
}
public class Food
{
static public void Eat() { }
}
// 델리게이트
public delegate void Attack(float damage);
public class AttackHandler
{
public Attack OnTtack;
public void AttackExcute(float damage)
{
OnTtack?.Invoke(damage);
}
public void AddToAttack(Attack attck)
{
OnTtack += attck;
}
}
public class Player
{
public float hp;
public float damage;
public Player(float hp,float d)
{
this.hp = hp;
this.damage = d;
}
public void GetAttack(float damage)
{
hp -= damage;
Console.WriteLine($"{damage} 데미지를 얻었습니다.");
}
}
public class SystemAlarm
{
public void UpdateUi(float damage)
{
Console.WriteLine($"player가 {damage} 를 입었습니다.");
}
}
static void Main(string[] args)
{
//
AttackHandler AKHandler = new AttackHandler();
// 인스턴스 생성
Player player = new Player(10f, 3f);
SystemAlarm alarm = new SystemAlarm();
AKHandler.AddToAttack(player.GetAttack);
AKHandler.AddToAttack(alarm.UpdateUi);
// 델리게이트 실행
AKHandler.AttackExcute( 7f );
}