상수는 불변의 값이다.

C#의 경우 기본 타입을 상수로 정의할 수 있다.

기본 타입은

Bool, Char, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, String 등이 있다.

 

상수의 특성

1. 컴파일 타임에서 정의가 된다.

2. 컴파일러는 해당 어셈블리의  메타데이터 안에 정의된 상수 기호를 검색하여 상수의 실제 값을 추출한 후 IL코드에 직접 첨부한다.

   (외부 dll에서 정의된 상수 값도 컴파일러가 해당 응용프로그램을 컴파일 시 외부 dll의 어셈블리의 메타데이터의 상수 기호를 검색하여 응용프로그램의 IL 코드 직접 첨부한다.)

3. 상수값에 대해서 따로 메모리 추가, 삭제를 하지 않는다.

 

 

주의해야 하는 코드를 보겠다.

 

외부에서 생성한 DLL 코드

ExportDLLClass 코드

DLL을 사용하는 응용프로그램

 

응용프로그램 코드

 

위의 응용프로그램을 컴파일 해보겠다. 

 

ExportDLLClass의 const 값 출력
실제 TestDLL 과 같은 폴더에 있는 응용프로그램 DLLTestConsole

이처럼 dll을 참조하여 사용시 응용프로그램은 참조할 dll을 응용프로그램의 경로에 복사시킨다.

그럼 저 경로의 TestDll.dll만 바꿔보겠다.

TestDLL의 ExportDLLClass의 Const값 변경
dll 코드 빌드 후 변경된 DLL만 덮어쓰기

위의 작업 후 DLLTestConsole을 실행하면 어떤 값이 나올까?? 우리 예상은 15이다 참조할 dll의 const 값이 15이기 때문이다.

dll을 변경해도 const값이 그대로 인 결과

위의 결과가 나온다. 이는 2번에서 설명한 응용프로그램에서의 IL코드가 재갱신되지 않았기 때문이다.

자 그럼 응용프로그램을 컴파일 후 실행해보겠다.

우리가 원하던 값 15 출력

이처럼 외부 dll에서 제공하는 const값은 참조하는 응용프로그램과의 혼동을 유발시킬 수 있다.

그래서 dll 개발 시 가능하면 const는 절대 불변의 값(Ex. PI = 3.141592...)만을 사용하는 것을 추천한다.

추 후 런타임에서 정의되는 상수 readonly도 포스팅하겠다.

 

결론

1. 상수는 컴파일 타임에서 해당 응용프로그램의 IL 코드로 생성된다.

2. 상수는 컴파일 타임에서 메모리를 이미 잡고 있기 때문에 런타임에서 사용하는 메모리와는 무관하다.

3. DLL을 배포하는 코드를 작업 시 가능하면 const는 사용하지 않는다. 정말 필요할 때 (PI = 3.141592, ushort max = 65535 등..)만 사용 하자.

'Program > C#' 카테고리의 다른 글

값타입의 박싱과 언박싱  (1) 2020.12.14
값 타입, 참조 타입  (0) 2020.12.13
네임스페이스와 어셈블리  (1) 2020.12.05
타입에 대하여2  (0) 2020.12.02
타입에 대하여1  (1) 2020.11.29

박싱

값 타입(Value Type)이나 참조 타입(Reference Type)을 System.Object로 변환하는 작업.

 

언박싱

박싱한 System.Object 타입을 박싱 이전의 타입으로 변환하는 작업.

 

그중 오늘은 값 타입의 박싱 연산, 언박싱 연산에 대해 포스팅을 하겠다.

 

값 타입과 참조 타입에 대해서는 이전 포스팅을 참고하길 바란다.

computervision-is-fun.tistory.com/21

 

값 타입, 참조 타입

1. 값 타입 (struct, enum)  - new 예약어를 통해 할당시킨 값 타입은 스레드 스택 메모리에 저장되므로, 스레드가 종료될 시 스택의 순서대로  삭제 된다. (GC의 오버헤드가 없다)  - = 연산시 필드 값

computervision-is-fun.tistory.com

 

값 타입의 박싱

값 타입은 생성 시 스레드의 스택 메모리에 저장되고, 참조 값이 아닌 값 자체를 다룬다.

참조 타입과 여러모로 대조가 된다.

이런 설명을 전재로 아래와 같이 값 타입을 ArrayList에 저장시키면 어떤 동작이 일어나까??

struct Point
{
    int nPtX;
    int nPtY;
    
    public Point(int nInputPtX, int nInputPtY)
    {
    	nPtX = nInputPtX;
        nPtY = nInputPtY;
    }
}

public static void Main()
{
    Point pt = new Point(1,2);
    ArrayList arrList = new ArrayList();
    
    arrList.Add(pt);
}

ArrayList의 Add함수를 살펴보자

public virtual Int32 Add(Object Value);

이처럼 Add함수는 Object를 매개변수로 동작한다.

그리고 위 코드는 참조 타입 매개변수에 값 타입을 넣어도 동작이 되었다.

왜 그럴까??

이유는 값 형식의 Point를 참조 형식의 Object로 박싱 명령을 내부적으로 진행했기 때문이다.

박싱은 내부적으로 어떤 동작을 할까??

어떻게 스레드 스택 메모리에 저장되어있는 데이터가 관리되는 힙 메모리에 저장되는 것일까?

순서는 이렇다.

 

1. 관리되는 힙 메모리에 Point의 필드 멤버를 고려하여 메모리가 할당된다. 이때 참조 형식이므로 타입 객체 포인터와 동기화 블록 인덱스라는 필드를 포함하여 할당한다.

2. 값 타입인 Point의 필드 멤버 변수들을 관리되는 힙 메모리에 복사시킨다.

3. 새로 생성된 참조 타입의 Point의 포인터를 반환한다.

 

이제 실제 위의 동작을 하는지 ILDASM.EME(역어 셈)으로 실행해보겠다.

위의 코드를 역어셈, IL_0011~IL0012가 박싱 동작

아쉽게도 더욱 자세한 코드는 보기 힘들 것 같다. 하지만 위와 같이 box라는 부수적인 명령어가 붙으면서 우리는 박싱 동작을 짐작할 수 있다. 박싱 동작을 더욱 자세히 확인할 수 있는 방법은 더 찾아봐야겠다.

 

추가로 박싱 된 참조 타입의 Point는 기존의 값 타입의 Point보다 생명주기가 길다.

이유는 관리되는 힙 메모리가 저장되었기 때문에 다음 GC의 호출을 기다리기 때문이다.

 

값 타입의 언박싱

Object에 저장되어있던 참조 형식의 Point가 값 형식의 Point로 변환하는 작업을 말한다.

CLR은 언박싱을 수행하면 아래와 같은 동작을 한다.

 

1. 관리되는 힙 메모리에서 참조 형식의 Point의 필드 값들의 주소를 가져온다.

  (ex nPtX, nPtY)

2. 이 값들은 스레드 스택 메모리의 Point에 복사시킨다.

 

위의 과정과 아래의 코드를 역어 셈으로 보겠다.

static void Main(string[] args)
{
    Point pt = new Point(1, 2);
    Object o = pt;
    pt = (Point)o;
}

 

IL_0012가 언박싱 동작

IL_0012 명령 줄을 보면 unbox라 하고 Object를 Point의 값 타입으로 복사시킨 것을 알 수 있다.

 

결론

값 타입의 박싱과 언박싱 모두 의도하지 않게 복사 동작을 진행한다. 따라서 우리는 값 타입의 박싱과 언박싱을 최소화해야 한다. 

 

 

 

 

 

 

 

'Program > C#' 카테고리의 다른 글

const 상수  (1) 2020.12.20
값 타입, 참조 타입  (0) 2020.12.13
네임스페이스와 어셈블리  (1) 2020.12.05
타입에 대하여2  (0) 2020.12.02
타입에 대하여1  (1) 2020.11.29

1. 값 타입 (struct, enum)

 - new 예약어를 통해 할당시킨 값 타입은 스레드 스택 메모리에 저장되므로, 스레드가 종료될 시 스택의 순서대로

   삭제 된다. (GC의 오버헤드가 없다)

 - = 연산시 필드 값이 모두 복사 동작을 한다. 일반적으로 16Byte 넘을 시 참조 타입(Class)으로 사용을 권한다. (Call by Value

 - System.ValueType으로 부터 항상 상속된다. (System.ValueType은 결국 System.Object로부터 상속되지만 Equals 메     서드를 재정의 함.)

 - 값 타입을 기본 타입으로 사용하여 새로운 값 타입이나 참조 타입을 정의 할 수 없다.

   (암묵적으로 seald라고 보면 된다.)

 - 런타임 중 NullReference Exception이 발생하지 않는다.

    (값 타입은 기본적으로 필드 멤버들은 모두 0으로 초기화 하기 때문 하지만 선언만 하고 정의를 안 할 시 컴파일러가

    오류 처리 해줌)

 

2. 참조 타입 (class)

 - new 예약어를 통해 할당시킨 참조 타입은 GC의 관리 메모리(힙 메모리)에 저장되며, 더 이상 사용을 안 할 시 GC가

   삭제한다.

 - = 연산 시 참조 타입의 포인터만 저장시킨다. (Call by Reference

 - System.Object로부터 상속된다.

 - 가상 함수, Base Class 등 상속시키거나 상속받을 수 있다.

 - 런타임 중 NullReference Exception이 발생한다.

 

코드 작성 시 struct를 쓰면 더 좋은 상황

 - 변경하지 않는 구조 일 경우 (Immutable Type)

 - 다른 타입으로부터 상속받을 필요가 없는 타입일 경우

 - 타른 타입의 기본 타입으로 쓰이게 하지 않을 경우

 - 인스턴스의 전체 크기가 약 16byte 미만일 경우

 - 함수의 매개변수나 반환 타입으로 사용할 일이 별로 없는 경우

 

 

 

'Program > C#' 카테고리의 다른 글

const 상수  (1) 2020.12.20
값타입의 박싱과 언박싱  (1) 2020.12.14
네임스페이스와 어셈블리  (1) 2020.12.05
타입에 대하여2  (0) 2020.12.02
타입에 대하여1  (1) 2020.11.29

이번 주제는 실제 코딩보다는 환경 설정 문제, 아키텍처 설계 문제에 직면할만한 문제 중 하나로 포스팅했다

 

네임스페이스 : 서로 다른 타입들의 특성과 성향을 논리적으로 그룹화하기 위한 수단이다.

어셈블리       : 네임스페이스에 그룹화 되어 있는 타입의 구현체 (ex. exe, dll)로 보면 된다.

 

using System.IO; // IO 기능을 위한 네임스페이스
using System.Text; //Text 기능을 위한 네임스페이스

FileStream fs = new FileStream(...);
StringBuilder sb = new StringBuilder();

c# 개발자들은 위와같은 using 지시자 구문으로 네임스페이스를 지정한다. 만약 네임스페이스 가없다면 아래와 같이 복잡하게 작성해야 한다.

System.IO.FileStream fs = new System.IO.FileStream(...);
System.Text.StringBuilder sb = new System.Text.StringBuilder();

CLR은 C# 코드 중 using 구문은 신경 쓰지 않는다. 왜냐하면 컴파일 단계에서 C# 컴파일러가 작성된 using 구문으로 메서드의 반환 값, 인수의 개수와 타입을 탐색하며 두 번째와 같은 코드로 변경시키기 때문이다.

실제로 컴파일러는 FileStream fs와 같은 문구를 만나면 using 지시자에 정의되어있는  System.IO, System.Text를 탐색해 FileStream 타입이 있는지 찾아낸다. 이런 컴파일러의 동작 덕분에 우리 개발자들은 조금 더 깨끗한 코드를 작성할 수 있다.

 

System.IO.FileStream 타입을 찾아낸 컴파일러는 그다음 무엇을 할까??

 

바로 어셈블리를 탐색 한다. 그리고 특정 타입에 대한 정보와 일치하는 어셈블리를 발견하면 어셈블리 정보와 타입 정보가 만들어지는 관리 모듈의 메타 데이터상에 기록시킨다. 그리고 CLR은 관리 모듈메타 데이터를 읽고 수행하는 것이다.

C# 컴파일러는 기본적으로 MSCORLIB.DLL어셈블리를 따로 지정하지 않더라도 자동으로 찾아볼 대상에 포함시키고 있다.

MSCORLIB.DLL에는 무엇이 있을까??

 

C# MSDN에서 기본 타입에 대해 조금만 찾아봐도 참조한 어셈블리에 포함되어있다.

docs.microsoft.com/ko-kr/dotnet/csharp/

 

C# 문서 - 시작, 자습서, 참조.

프로그래밍 C# 알아보기 - 초보 개발자, C#을 처음 사용하는 개발자, 숙련된 C# / .NET 개발자용

docs.microsoft.com

 

MSCORLIB.DLL은 기본적으로 Object, Int32, double, String 등 핵심적인 타입들은 모두 포함하는 FCL이 들어있다.

 

네임스페이스를 씀으로 써 일반적으로 겪을 수 있는 문제

회사 A와 회사 B가 제공하는 DLL을 동시에 쓴다고 할 때 A사의 Widget, B사의 Widget을 쓰는 상황

using A;
using B;

Widget w = new Widget(); //A.Widget? B.Widget?

위의 코드는  컴파일러가 A.Widget, B.Widget의 모호한 참조 오류가 발생한다.

그래서 위의 모호성을 없애기 위해서 1번 해결 방법은 using 지시자 구문을 쓰지 않는 것이다.

A.Widget w = new A.Widget(); //A.Widget

그리고 두 번째 해결 방법

using AWidth = A.Widget;
AWidth w = new AWidth(); //A.Widget

이렇게 우리는 참조의 모호성을 해결했다.

일반적으로 제3자가 사용하는 타입을 설계하는 경우, 반드시 이러한 타입들을 별도의 네임스페이스상에 정의하여 컴파일러가 쉽게 타입을 구분할 수 있도록 설계해야 한다. 위의 예시에서 안 좋게 나왔지만 사실 주요 방법은 앞에 회사명.으로 시작하는 네임스페이스로 지시하는 방법이 일반적이다. 실제 Microsoft도 Microsoft.CSharp, Microsoft.VisualBasic을 쓴다고 한다.

 

추가로 C#코드에서 네임스페이스 선언 방법이다

namepsace Name{
    public Class A{...} //Name.A
    
    namespace Donghee
    {
        public class B{...} //Name.Donghee.B
    }
}

네임스페이스와 어셈블리 사이의 연관성

네임스페이스와 어셈블리에 대해서는 필요 불충분의 관계이고, 이는 연관성이 별로 없다는 뜻이다. 무슨 말이냐 하면 System.IO.FileStream 클래스 타입은 mscrolib.dll에 System.IO.FileSystemWatcher 클래스 타입은 System.dll에 들어 있다. 즉 앞에 System.IO가 동일하다고 해서 동일한 어셈블리에 포함되어 있는 것이 아니다. 추가로 하나의 어셈블리에 2개 이상의 네임스페이스가 포함될 수도 있다. (ex. System.Int32, System.Text.StringBuilder는 mscrolib.dll에 같이 정의되어있음)

정리하자면

 

- 네임스페이스가 같다고 하나의 어셈블리에 포함되어 있지 않다.

- 하나의 어셈블리에 2개 이상의 네임스페이스가 포함될 수 있다.

 

예시 Class ResXFileRef 

네임스페이스 :  System.Resources

어셈블리      :  System.Windows.Forms.dll

 

docs.microsoft.com/ko-kr/dotnet/api/system.resources.resxfileref.-ctor?view=net-5.0

 

ResXFileRef 생성자 (System.Resources)

ResXFileRef 클래스의 새 인스턴스를 초기화합니다.Initializes a new instance of the ResXFileRef class.

docs.microsoft.com

 

'Program > C#' 카테고리의 다른 글

const 상수  (1) 2020.12.20
값타입의 박싱과 언박싱  (1) 2020.12.14
값 타입, 참조 타입  (0) 2020.12.13
타입에 대하여2  (0) 2020.12.02
타입에 대하여1  (1) 2020.11.29

타입간 캐스팅

코딩을 하다보면 타입간 캐스팅을 해야하는 상황이 빈번하게 발생한다. 캐스팅은 일반적으로 

Up Casting, Down Casting기능으로 쓰인다.

 

Up casting

- 파생된 객체가 베이스 클래스로 변환하는 캐스팅

Down casting  

- 베이스 클래스에서 파생 클래스로 캐스팅

 

//System.Object에서 파생 된 base
public class base
{
	
}

//base에서 파생 된 derived
public class derived : base
{
    
}

class Program
{
    static void Main(string[] args)
    {            
        object ob = new derived(); //upcasting
        Console.WriteLine(ob.GetType().ToString());
        //derived 출력
        
        Base b = (Base)ob;        //downcasting
        Console.WriteLine(b.GetType().ToString());
        //derived 출력
        
        derived d = (derived)b;   //downcasting
        Console.WriteLine(d.GetType().ToString());
        //derived 출력
    }

}

 

위의 코드는 캐스팅의 예시 이다. CLR기준으로 순서대로 설명을 해보자면

 

1. Main 함수 시작 후 CLR은 derived 인스턴스를 생성하여 인스턴스 포인터를 반환한다.

 

2. 저장할 인스턴스의 클래스 형태는 object이므로 CLR은 derived 객체가 object로 업캐스팅이 가능한지 검사 후 업캐스팅 하여 인스턴스 ob에 저장한다. 

 

3. 인스턴스 ob의 타입을 출력 한다. ob는 derived에서 이미 지정된 GetType을 가지고 있으므로 derived 출력

 

4. 저장할 인스턴스의 클래스 형태는 Base이므로 CLR은 derived 객체가 Base로 다운 캐스팅이 가능한지 검사 후 다운캐스팅 하여 인스턴스 b에 저장한다.

 

5. 인스턴스 b의 타입을 출력 한다. b는 derived에서 이미 지정된 GetType을 가지고 있으므로 derived 출력

 

6. 저장할 인스턴스의 클래스 형태는 derived이므로 CLR은 derived 객체가 derived로 다운 캐스팅이 가능한지 검사 후 다운캐스팅 하여 인스턴스 d에 저장한다.

 

7. 인스턴스 d의 타입을 출력 한다. d는 derived에서 이미 지정된 GetType을 가지고 있으므로 derived 출력

 

CLR은 매번 캐스팅 할때 인스턴스의 현재 타입과 변환 할 타입의 상속구조, 기본타입을 비교한다.

때문에 캐스팅은 변환 타입에 따라 로드가 걸릴 수 밖에 없다. 하지만 이는 프로그램의 무결성과 보안성을 보장하기 위해 필요한 작업고, Object의 GetType 메서드가 비가상 메서드인 이유와 동일하다. 

//System.Object에서 파생 된 base
public class base
{
	
}


class Program
{
    static void Main(string[] args)
    {            
        object ob = 3;
        Base b = (Base)ob;  // InvalidCastEception 런타임 Error 발생      
    }

}

위는 InvalidCastException 런타임 에러가 발생하는 구조이다.

이유는 CLR이 int형 타입이 들어간 ob가 연관성 없는 Base Class로 캐스팅을 진행했기 때문이다.

이런 런타임 에러는 프로그램이 죽는 결과를 보여주며 해결방법은 try/catch문으로 예외를 처리해주거나

개발자가 주의하는 방법 뿐이다.

 

is와 as 연산자를 이용한 캐스팅

위에서 설명했던 명시적 캐스팅은 개발자의 실수로 런타임 에러가 발생할 가능성이 존재한다.

그래서 C#은 is 와 as 연산자를 지원한다.

 

is : 확인 하고자 하는 인스턴스의 타입 변환 가능유무를 true, false로 반환 해준다.

as : 인스턴스를 변환 하고자 하는 타입으로 변환 시킨다 성공시 변환 타입을 반환 하고 실패시 null을 반환 한다.

 

is 연산자 예시 코드

if(o is Employee)
{
	Employee e = (Employee)o;  
}

 

as 연산자 예시 코드

Employee e = o as Employee;

if(e == null)
	return;

조금더 설명을 해보자면 is는 CLR이 변환 하고자 하는 타입과 검사를 수행하고 bool 형식으로만 반환 시킨다.

다시 말해 is 연산자 예시 코드

1. if(o is Employee)

2. Employee e = (Employee)o; 

총 2번 CLR이 타입에 대해 검사를 수행한다. 

그래서 성능상으로 바로 as 연산자를 이용해서 변환 후 null 체크만 진행 하는 코드가 더 빠르다.

 

결론 

1. 캐스팅은 CLR이 타입 간 상속 관계, 기본 타입 관계를 비교하여 변환 가능하면 변환, 불가능 하면 InvalidCastException 예외를 던진다.

2. is 보다는 as를 이용하여 null 체크 하는 것이 빠르다.

 

'Program > C#' 카테고리의 다른 글

const 상수  (1) 2020.12.20
값타입의 박싱과 언박싱  (1) 2020.12.14
값 타입, 참조 타입  (0) 2020.12.13
네임스페이스와 어셈블리  (1) 2020.12.05
타입에 대하여1  (1) 2020.11.29

- 모든 타입은 System.Object를 상속한다.

class Derived {
	...
}
class Derived : System.Object {
	...
}

위의 두 Class는 같은 코드이다. 이와 같은 약속으로 모든 타입은 System.Object의 public 메서드, protected 메서드를 그냥 사용할 수 있고, override 하여 사용할 수도 있다.

아래 표는 System.Object의 메서드 표이다.

Public 메서드 설명
Equals 인수 인스턴스와 동일한 값을 가지는 경우 true 아니면 false 를 반환 한다.
ToString 현재 타입의 전체 이름을 문자열로 반환한다.
override하여 객체의 상태  또는 테스트 코드 등으로 사용하는데 현재 스레드와 연결된 CaltureInfo의 설정에 따라 적절한 지역 설정을 반영하여 반환하는 것이 좋다고 한다.
GetHashCode 객체의 값으로부터 해시 코드를 만들어 반환한다. Dictionary와 같이 해시 테이블 컬렉션에서 해당 객체를 사용하기 위해서는 override하여 해시 키값의 유일성을 만족하는 함수를 작성 해야한다. 
GetType GetType 메서드를 호출하기 위하여 사용된 객체의 타입을 설명하는 Type이라는 타입으로 부터 파생된 객체의 인스턴스를 반환한다. 여기서 반환되는 Type객체는 객체의 타입의 관한 메타데이터를 가지고 있으며 이를 사용하기 위해 Reflection 클래스들과 함께 활용할 수 있다. 비가상 메서드이며 이는 타입에 대한 정보를 속이거나 잘못 전달하여 타입 안정성을 위반하지 않도록 예방한 것이다. 
Protected 메서드 설명
MemberwiseClone 비가상 메서드로 현재 인스턴스와 동일한 타입, 동일한 값을 모드 재할당하여 생성된 인스턴의 포인터를 반환한다.
그냥 Clone 기능이라 보면 편하다.
Finalize GC가 해당 객체는 더이상 어디에서도 참조 되지 않고 사용하지 않는다고 판단하고 이 객체에 대한 메모리 공간을 회수하는 요청이 들어왔을때 호출 된다. 비관리 리소스를 가지고 있는 객체일 경우 재정의 해야한다.   

- 값 타입을 제외한 참조 타입은 모드 new 연산자를 통해 생성한다.

Employee e = new Employee("Hong")

위와 같은 명령어가 발생하면 CLR은 아래와 같은 순서로 진행시킨다.

1. 할당하려는 타입(System.Object를 포함)하여 정의된 모든 필드들을 메모리에 할당하기 위한 바이트 수를 계산한다.

2. 별도의 타입 객체 포인터(Type Object Pointer)와 동기화 블록 인덱스(Sync Block Index)가 추가되며 이는 CLR에 의해 객체를 관리하기 위해 사용되며 추가된 멤버 값들은 실제 객체의 크기에 포함된다.

더보기

🎫계산된 Byte의 수

System.Object 필드

Emplyee 필드

Type Object Pointer

Sync Block Index

3. 계산된 Byte의 수만큼 관리되는 힙으로부터 객체를 위하여 메모리를 할당하며, 처음 할당할 때에는 모든 Byte를 0으로 초기화한다.

4. 타입 객체 포인터, 블록 인덱스 멤버를 초기화한다.

5. 생성자와 인수(Hong)가 Employee 생성자 중 일치하는 인수 형태를 지닌 생성자가 실행된다. 위와 같은 경우는 Emplyee(string strNmae)이 되겠다. 상속받은 객체가 있다면 상속받은 Super Class의 생성자부터 실행되겠다.

6. 특이하게도 생성자의 마지막은 System.Object의 생성자를 부르게 되며, 이 생성자는 하는 일 없이 반환된다.

7. 모든 작업이 끝나면 생성된 객체의 포인터를 반환한다.

 

C#의 객체를 사용하는 우리는 알게 모르게 객체의 포인터를 다룬 것이다. 

그래서 일반적인 함수에 인수가 객체일 경우 Call by Reference를 사용하게 됐다.

 

결론

- 모든 타입은 System.Object를 상속받는다.

- new 연산자는 모든 초기화를 완료한 후 객체의 포인터를 반환시킨다.

 

'Program > C#' 카테고리의 다른 글

const 상수  (1) 2020.12.20
값타입의 박싱과 언박싱  (1) 2020.12.14
값 타입, 참조 타입  (0) 2020.12.13
네임스페이스와 어셈블리  (1) 2020.12.05
타입에 대하여2  (0) 2020.12.02

+ Recent posts