C #에서 참조 유형과 값 유형의 차이점은 무엇입니까?


100

몇 달 전에 어떤 사람이이 질문을했지만 자세히 설명 할 수 없었습니다. C #에서 참조 형식과 값 형식의 차이점은 무엇입니까?

나는 그 값 유형을 알고 int, bool, float, 등 참조 유형은 delegate, interface, 등 또는도이 잘못?

전문적인 방식으로 설명해 주시겠습니까?


3
작은 메모로 C #에 대한 질문이라고 생각하지만 실제로는 C # + .NET에 관한 것입니다. .NET을 분석하지 않고는 C #을 분석 할 수 없습니다. 다른 하나를 분석하지 않고 하나를 분석 할 때 몇 가지 요점이있을 수 있기 때문에 질문에 다시 태그를 지정하지 않을 것입니다 (반복자 및 클로저, 당신을보고 있습니다)
xanatos

@xanatos C #, VB.Net 및, Net이 공통으로 가지고있는 CLI에 대한 질문입니다. CLI에 대한 태그가 있어야하지만 CLI는 다른 작업에 사용됩니다. CLR이 있지만 표준이 아닌 구현입니다.
user34660

답변:


172

귀하의 예 때문에 동안 약간의 홀수 int, boolfloat특정 유형, 인터페이스 및 대의원이 있습니다 가지 유형의 - 단지 같은 structenum값 형식의 종류입니다.

이 기사에서 참조 유형 및 값 유형 대한 설명을 작성 했습니다 . 혼란스러워하는 부분이 있으면 기꺼이 확장하겠습니다.

"TL; DR"버전은 특정 유형의 변수 / 표현식 값이 무엇인지 생각하는 것입니다. 값 유형의 경우 값은 정보 자체입니다. 참조 유형의 경우 값은 null 일 수있는 참조이거나 정보를 포함하는 개체를 탐색하는 방법 일 수 있습니다.

예를 들어, 변수를 종이처럼 생각하십시오. "5"또는 "false"라는 값이 적혀있을 수 있지만 내 집은 가질 수 없습니다 . 내 집 으로 가는 을 알려야합니다. 이러한 지침은 참조와 동일합니다. 특히 두 사람이 내 집으로가는 방향이 같은 다른 종이를 가지고있을 수 있습니다. 한 사람이 그 길을 따라 내 집을 빨간색으로 칠하면 두 번째 사람도 그 변화를 볼 수 있습니다. 둘 다 종이에 내 집 사진 을 따로 찍었 다면 한 사람이 종이에 색칠을하더라도 다른 사람의 종이를 전혀 바꾸지 않을 것입니다.


2
사물이 제공 할 수있는 세 가지 기본 유형의 의미 체계가 있음에 유의하는 것이 중요합니다. 즉, 변경 불가능한 의미론, 변경 가능한 값 의미론 및 변경 가능한 참조 의미론입니다. 개념적으로 사물이 구현하는 의미 체계의 종류는 독립 실행 형 힙 개체로 저장되는지 아니면 변수 / 필드 (구조체)로 저장되는지에 대해 직교합니다. 실제로 필드를 노출하지 않는 구조체는 모든 종류의 의미 체계를 구현할 수 있지만 .net이 힙 참조의 무차별 공유를 허용한다는 사실은 힙 개체가 가변 값 의미 체계를 구현할 수 없음을 의미합니다.
supercat

이 비트를받지 못했습니다 while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types. int, bool이 특정 유형이라는 것은 무엇을 의미합니까? int, bool, float, class, interface, delegate 등 C #의 모든 것은 유형 (정확한 데이터 유형)입니다. 데이터 형식은 C #에서 '참조 형식'과 '값 형식'으로 구분됩니다. 그렇다면 int가 특정 유형이지만 인터페이스가 일종의 유형이라고 말하는 이유는 무엇입니까?
RBT

2
@RBT : 데이터 유형은 아니다 단지 "참조 형"및 "값 유형 '으로 분리. 또한 "class, struct, enum, delegate, interface"로 분리됩니다. int구조체, string클래스, Action대리자 등입니다. "int, bool, float, class, interface, delegate"목록은 "10, int"와 같은 방식으로 다른 종류의 항목을 포함하는 목록입니다. 다른 종류의 것들을 포함하는 목록.
Jon Skeet

@JonSkeet 아마도이 게시물 의 답변 은 약간 오해의 소지가 있습니다.
RBT

@RBT : 어쩐지 나쁜 말이지 만 끔찍하지는 않다고 말하고 싶습니다.
Jon Skeet

26

값 유형 :

메모리 주소가 아닌 일부 값을 보유합니다.

예:

구조

저장:

TL; DR : 변수의 값은 해제 될 때마다 저장됩니다. 지역 변수는 예를 들어 스택에 살고 있지만 구성원으로 클래스 내에 선언 때 단단히가 선언 된 클래스와 결합 된 힙에 살고있다.
: 그들이 선언 된 곳 따라서 값 유형이 저장됩니다. 예 : int지역 변수로서 함수 내부의의 값은 스택에 저장되는 반면 int, 클래스의 멤버로 선언 된 in의 값은 선언 된 클래스와 함께 힙에 저장됩니다. 클래스에는 선언 된 클래스와 정확히 동일한 라이프 타입이 있으므로 가비지 수집기의 작업이 거의 필요하지 않습니다. 하지만 더 복잡합니다. @JonSkeet의 책 " C # In Depth "를 참조하겠습니다.보다 간결한 설명을 위해 .NET의 메모리 "

장점 :

값 유형에는 추가 가비지 콜렉션이 필요하지 않습니다. 그것은 존재하는 인스턴스와 함께 가비지 수집을 가져옵니다. 메서드의 지역 변수는 메서드를 떠날 때 정리됩니다.

단점 :

  1. 큰 값 집합이 메서드에 전달되면 수신 변수가 실제로 복사하므로 메모리에 두 개의 중복 값이 ​​있습니다.

  2. 수업을 놓치면 모든 oop 혜택이 손실됩니다.

참조 유형 :

값이 아닌 값의 메모리 주소를 보유합니다.

예:

수업

저장:

힙에 저장

장점 :

  1. 참조 변수를 메소드에 전달하면 실제로 원래 값이 변경되는 반면 값 유형에서는 주어진 변수의 사본이 취해져 그 값이 변경됩니다.

  2. 변수의 크기가 클 때 참조 형이 좋다

  3. 클래스는 참조 유형 변수로 제공되므로 재사용 가능성을 제공하므로 객체 지향 프로그래밍에 도움이됩니다.

단점 :

값을 읽을 때 할당 및 역 참조시 더 많은 작업 참조 가비지 수집기에 대한 추가 오버로드


5
참조 유형이 힙에 저장되고 값 유형이 스택에 저장되는 것은 사실이 아닙니다. 자세한 내용은 yoda.arachsys.com/csharp/memory.html을 참조하십시오.
Rhys

1
이 답변에는 많은 오해가 있습니다. C #을 통해 Jeff Richters CLR을 읽으십시오. 값 유형은 스레드 스택에 저장되며 GC (가비지 수집)의 대상이 아닙니다. GC와 관련이 없습니다. 참조 유형은 관리되는 힙에 저장되므로 GC의 적용을받습니다. 참조 유형에 루트 참조가 있으면 수집 할 수 없으며 0, 1 및 2 세대로 승격됩니다. 루트 참조가 없으면 가비지 수집이 가능하며 그런 다음 부활이라고하는이 프로세스를 거칩니다. 살해되고 다시 살아 나고 마침내 수집됩니다.
Jeremy Thompson

13

컴퓨터가 메모리에 물건을 할당하는 방법을 알고 포인터가 무엇인지 안다면 두 가지의 차이점을 더 쉽게 이해할 수있었습니다.

참조는 일반적으로 포인터와 연관됩니다. 변수가있는 메모리 주소가 실제로 다른 메모리 위치에있는 실제 객체의 다른 메모리 주소 를 보유하고 있음을 의미합니다 .

제가 드리려고하는 예는 지나치게 단순화되어 있으므로 소금 한 알과 함께 가져 가십시오.

컴퓨터 메모리가 내부에 무언가를 담을 수있는 한 줄의 사서함 (PO Box 0001부터 PO Box n으로 시작)이라고 상상해보십시오. 사서함이 당신을 위해 그것을하지 않으면 해시 테이블이나 사전 또는 배열 또는 유사한 것을 시도하십시오.

따라서 다음과 같은 작업을 수행 할 때 :

var a = "Hello";

컴퓨터는 다음을 수행합니다.

  1. 메모리를 할당하고 (예를 들어 메모리 위치 1000에서 5 바이트로 시작) H (1000에서), e (1001에서), l (1002에서), l (1003에서) 및 o (1004에서)를 입력합니다.
  2. 메모리 어딘가 (예 : 0500 위치)에 할당하고 변수 a로 할당합니다.
    그래서 그것은 일종의 별칭과 같습니다 (0500은 a).
  3. 해당 메모리 위치 (0500)의 값을 1000 (문자열 Hello가 메모리에서 시작하는 위치)에 할당합니다. 따라서 변수 a는 "Hello"문자열의 실제 시작 메모리 위치에 대한 참조 를 보유 합니다.

값 유형은 실제 사물을 메모리 위치에 보관합니다.

따라서 다음과 같은 작업을 수행 할 때 :

var a = 1;

컴퓨터는 다음을 수행합니다.

  1. 0500에 메모리 위치를 할당하고 변수 a에 할당하십시오 (동일한 별칭)
  2. 그 안에 값 1을 넣으십시오 (메모리 위치 0500).
    실제 값 (1)을 유지하기 위해 추가 메모리를 할당하지 않습니다. 따라서 a는 실제로 실제 값을 보유하고 있기 때문에 값 유형이라고합니다.


@Jon, 글쎄, 내가 말한 것을 무효화합니다, LOL. 그러나 내가 말했듯이, 내 경우에는 도움이 된 두 가지 유형을 이해하는 것이 지나치게 단순화되었습니다. 적어도 그것이 내 마음 속에서 상상 한 방법입니다 :).
Jimmy Chandra

8

이것은 약 2 년 전 다른 포럼의 제 게시물입니다. 언어는 vb.net (C #과 반대)이지만 값 유형 대 참조 유형 개념은 .net 전체에서 동일하며 예제는 여전히 유지됩니다.

.net 내에서 모든 유형은 기술적으로 기본 유형 Object에서 파생된다는 사실을 기억하는 것도 중요합니다. 값 유형은 그렇게 작동하도록 설계되었지만 결국 기본 유형 Object의 기능도 상속합니다.

A. 값 유형은 메모리에서 개별 값이 저장되는 고유 한 영역을 나타냅니다. 값 유형은 고정 된 메모리 크기이며 고정 된 크기의 주소 모음 인 스택에 저장됩니다.

다음과 같은 진술을 할 때 :

Dim A as Integer
DIm B as Integer

A = 3
B = A 

다음을 수행했습니다.

  1. 32 비트 정수 값을 보유하기에 충분한 메모리에 2 개의 공간을 생성했습니다.
  2. A에 할당 된 메모리 할당에 값 3을 배치했습니다.
  3. A에 보유 된 것과 동일한 값을 할당하여 B에 할당 된 메모리 할당에 값 3을 배치했습니다.

각 변수의 값은 각 메모리 위치에 개별적으로 존재합니다.

B. 참조 유형은 다양한 크기가 될 수 있습니다. 따라서 "스택"에 저장할 수 없습니다 (스택이 고정 된 크기의 메모리 할당 모음임을 기억하십니까?). 이들은 "관리되는 힙"에 저장됩니다. 관리되는 힙의 각 항목에 대한 포인터 (또는 "참조")는 스택 (예 : 주소)에서 유지됩니다. 코드는 스택에서 이러한 포인터를 사용하여 관리되는 힙에 저장된 개체에 액세스합니다. 따라서 코드에서 참조 변수를 사용할 때 실제로 포인터 (또는 관리되는 힙의 메모리 위치에 대한 "주소")를 사용합니다.

문자열 Property Person.Name을 사용하여 clsPerson이라는 클래스를 만들었다 고 가정 해 보겠습니다.

이 경우 다음과 같은 진술을 할 때 :

Dim p1 As clsPerson
p1 = New clsPerson
p1.Name = "Jim Morrison"

Dim p2 As Person

p2 = p1

위의 경우 p1.Name 속성은 예상대로 "Jim Morrison"을 반환합니다. p2.Name 속성은 직관적으로 예상하는 것처럼 "Jim Morrison"도 반환합니다. 나는 p1과 p2가 모두 스택에서 별개의 주소를 나타낸다고 생각합니다. 그러나 이제 p2에 p1 값을 지정 했으므로 p1과 p2는 모두 관리되는 힙의 동일한 위치를 가리 킵니다.

이제 다음 상황을 고려하십시오.

Dim p1 As clsPerson
Dim p2 As clsPerson

p1 = New clsPerson
p1.Name = "Jim Morrison"

p2 = p1

p2.Name = "Janis Joplin"

이 상황에서 개체를 참조하는 스택의 포인터 p1을 사용하여 Managed Heap에 사람 클래스의 새 인스턴스 하나를 만들고 개체 인스턴스의 Name 속성에 "Jim Morrison"값을 다시 할당했습니다. 다음으로 스택에 또 다른 포인터 p2를 생성하고 p1이 참조하는 것과 동일한 주소를 관리되는 힙에 지정했습니다 (할당 p2 = p1을 만들 때).

여기에 트위스트가 있습니다. p2의 Name 속성에 "Janis Joplin"값을 할당하면 다음 코드를 실행 한 경우 p1과 p2가 모두 참조하는 개체의 Name 속성이 변경됩니다.

MsgBox(P1.Name)
'Will return "Janis Joplin"

MsgBox(p2.Name)
'will ALSO return "Janis Joplin"Because both variables (Pointers on the Stack) reference the SAME OBJECT in memory (an Address on the Managed Heap). 

그게 말이 되었나요?

마지막. 이렇게하면 :

DIm p1 As New clsPerson
Dim p2 As New clsPerson

p1.Name = "Jim Morrison"
p2.Name = "Janis Joplin"

이제 두 개의 별개의 Person Object가 있습니다. 그러나이 작업을 다시 수행하는 순간 :

p2 = p1

이제 두 가지 모두 "Jim Morrison"을 가리 켰습니다. (나는 p2가 참조하는 힙의 객체에 무슨 일이 일어 났는지 정확히 모르겠습니다... 나는 그것이 이제 범위를 벗어났다고 생각합니다. 이것은 누군가가 나를 바로 잡을 수있는 영역 중 하나입니다...). -편집 : 나는 이것이 새로운 할당을 만들기 전에 p2 = Nothing OR p2 = New clsPerson을 설정하는 이유라고 믿습니다.

다시 한 번, 지금 이렇게하면 :

p2.Name = "Jimi Hendrix"

MsgBox(p1.Name)
MsgBox(p2.Name)

이제 두 msgBox 모두 "Jimi Hendrix"를 반환합니다.

이것은 약간 혼란 스러울 수 있으며, 마지막으로 한 번 말씀 드리겠습니다. 세부 사항 중 일부가 잘못되었을 수 있습니다.

행운을 빕니다. 저보다 더 잘 아는 다른 사람들이이 중 일부를 명확히하는 데 도움이되기를 바랍니다. . .


왜 당신이 찬성 투표를받지 않았는지 모르겠습니다. 좋은 대답은 명확하고 간단한 예를 통해 이해하는 데 도움이되었습니다.
Harry

에 관해서는 값 타입 대 참조 타입 개념 그물에 걸쳐 균일 한, 그들은 실제로 공용 언어 인프라에 정의되어 있습니다 (CLI) 사양, 에크 표준 335 (또한 ISO 표준). 이것이 .Net의 표준 부분에 대한 표준입니다. Ecma 표준 334 (또한 ISO 표준)는 C # 언어이며 C # 구현이 CLI에 의존하거나이 C # 표준에 필요한 최소 CLI 기능을 얻는 대체 방법을 지원 해야한다고 명시 적으로 명시합니다 . 그러나 VB.Net은 표준이 아니며 Microsoft 소유입니다.
user34660

5

값 데이터 유형참조 데이터 유형

1) (데이터를 직접 포함)하지만 참조 (데이터를 나타냄)

2)에 (모든 변수는 그 자신의 복사본이 있음)하지만
기준 (기타 변수가 어떤 오브젝트를 참조 할 수있는 것보다)

3) (작업 변수는 다른 변수에 영향을 줄 수 없음 )이지만 참조 (변수는 다른 변수에 영향을 줄 수 있음)

4) 값 유형 은 (int, bool, float)이지만 참조 유형 은 (array, class objects, string)


2

값 유형 :

  • 고정 메모리 크기.

  • 스택 메모리에 저장됩니다.

  • 실제 값을 보유합니다.

    전의. int, char, bool 등 ...

참조 유형 :

  • 고정 메모리가 아닙니다.

  • 힙 메모리에 저장됩니다.

  • 실제 값의 메모리 주소를 저장합니다.

    전의. 문자열, 배열, 클래스 등 ...


1

"값 유형을 기반으로하는 변수는 값을 직접 포함합니다. 하나의 값 유형 변수를 다른 값에 할당하면 포함 된 값이 복사됩니다. 이는 객체 자체가 아닌 객체에 대한 참조를 복사하는 참조 유형 변수의 할당과 다릅니다." Microsoft의 라이브러리에서.

여기여기 에서보다 완전한 답을 찾을 수 있습니다 .


1
참조 유형과 값 유형에 대해 할당이 다르게 작동하는 것처럼 들리기 때문에 그 설명이 마음에 들지 않습니다. 그렇지 않습니다. 두 경우 모두 "대상"변수의 값을 표현식과 동일하게 만듭니다. 값이 복사됩니다. 차이가 복사됩니다 값이 참조가 참조 유형에 대한 - 그 값이 무엇인지에 있습니다. 그래도 여전히 변수의 가치입니다.
Jon Skeet

나는 당신에게 동의하며이 기사 에서 읽을 수 있듯이 다를 수 있음을 이미 알고 있습니다 . 그러나 나는 주제와 당신이 일반적으로 책에서 읽는 방법에 대한 Microsoft의 가이드를 무시하고 있습니다. 나를 비난하지 마십시오! :)
Lucas S.

오 물론입니다 ... 결함이있는 MSDN 문서가 많이 있습니다. :)
Jon Skeet

1

때로는 설명이 특히 초보자에게 도움이되지 않습니다. 값 유형을 데이터 파일로, 참조 유형을 파일의 바로 가기로 상상할 수 있습니다.

따라서 참조 변수를 복사하면 링크 / 포인터를 메모리 어딘가의 실제 데이터에만 복사합니다. 값 유형을 복사하면 실제로 메모리의 데이터를 복제합니다.


0

이것은 심오한 방식으로 잘못되었을 수 있지만 간단하게하기 위해 :

값 유형은 일반적으로 "값으로"전달되는 값입니다 (따라서 복사). 참조 유형은 "참조에 의해"전달됩니다 (따라서 원래 값에 대한 포인터 제공). .NET ECMA 표준에서는 이러한 "사물"이 저장되는 위치를 보장하지 않습니다. 스택이없는 .NET 구현 또는 힙이없는 구현을 빌드 할 수 있습니다 (두 번째는 매우 복잡하지만 파이버와 많은 스택을 사용하여 가능할 것입니다).

구조체는 값 유형 (int, bool ...은 구조체이거나 적어도 다음과 같이 시뮬레이션 됨)이고 클래스는 참조 유형입니다.

값 형식은 System.ValueType의 자손입니다. 참조 유형은 System.Object의 자손입니다.

이제 .. 결국 값 유형, "참조 된 객체"및 참조 (C ++에서는 객체에 대한 포인터라고합니다. .NET에서는 불투명합니다. 우리는 그것이 무엇인지 모릅니다. 우리의 관점에서 볼 때 그들은 개체에 대한 "핸들"). 이러한 마지막 항목은 값 유형과 유사합니다 (복사본으로 전달됨). 따라서 객체는 객체 (참조 유형)와 이에 대한 0 개 이상의 참조 (값 유형과 유사 함)로 구성됩니다. 참조가 0 개이면 GC가이를 수집 할 것입니다.

일반적으로 (.NET의 "기본"구현에서) Value 유형은 스택 (로컬 필드 인 경우) 또는 힙 (클래스의 필드 인 경우, 반복기 함수의 변수 인 경우)에서 이동할 수 있습니다. 클로저에 의해 참조되는 변수 인 경우, 비동기 함수에서 변수 인 경우 (최신 비동기 CTP 사용) ...) 참조 된 값은 힙으로 만 이동할 수 있습니다. 참조는 값 유형과 동일한 규칙을 사용합니다.

반복기 함수, 비동기 함수 또는 클로저에 의해 참조되기 때문에 힙에있는 Value Type의 경우 컴파일 된 파일을 보면 컴파일러가 이러한 변수를 넣는 클래스를 생성했음을 알 수 있습니다. , 클래스는 함수를 호출 할 때 빌드됩니다.

이제 나는 긴 글을 쓰는 법을 모르고 내 인생에서 더 좋은 일을 할 수 있습니다. "정확한" "학업 적" "올바른"버전을 원하면 다음을 읽으십시오.

http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

찾고 있어요 15 분이에요! 압축 된 "사용 준비 완료"기사이기 때문에 msdn 버전보다 낫습니다.


1
난해한 방식 이상으로 잘못되었습니다. 이것은 근본적으로 잘못된 것입니다. 참조 유형 값은 여전히 ​​값으로 전달되기 때문입니다. 값이 객체가 아니라 참조 일뿐입니다. pobox.com/~skeet/csharp/parameters.html을 참조하십시오 . 아, 그리고 지역 변수는 예를 들어 캡처되거나 반복기 블록의 일부인 경우 힙에서도 끝날 수 있습니다.
Jon Skeet

반복자 블록은 클래스로 변환되므로 "뒤에" "클래스의 필드"입니다. 클로저도 마찬가지입니다. 그래 ... 나는 "포인터"(참조)와 "뾰족한"사이의 구분을 쓰는 것을 잊었다
xanatos

@xanatos : 물론, 컴파일 후 클래스의 필드이지만 소스 코드에서는 여전히 로컬 변수입니다. 나는 또한 참조 자체를 "가치 유형"이라고 부르지 않을 것입니다. 나는 당신이 어디에서 왔는지 알고 있다고 생각하지만, 이런 식으로 물을 진흙탕으로 만드는 것은 좋은 생각이 아니라고 생각합니다.
Jon Skeet

@jon 그래 ... 포인터는 .net에서 "불투명"하고 ValueType에서 파생되지 않기 때문에 세 번째 유형입니다. 그러나 그들은 참조보다 값 유형과 더 유사합니다. 당신은 그들을 "ref"하고 "out"할 수 있습니다. 나는 "누군가"가 반복자의 작업을 꼼꼼히 선택해야했기 때문에 물을 진흙탕으로 만들어야했다.
xanatos

내가 지금 가리키는 기사를 보면 "(1) 값 유형의 인스턴스, (2) 참조 유형의 인스턴스 및 (3) 참조의 세 가지 종류의 값이 있습니다. (C #의 코드는 조작 할 수 없습니다. 참조 유형의 인스턴스를 직접; 항상 참조를 통해 수행합니다. 안전하지 않은 코드에서 포인터 유형은 값의 저장 요구 사항을 결정하기 위해 값 유형처럼 처리됩니다. ) ".
xanatos

0

참조 유형을 생각하는 가장 간단한 방법은 "객체 ID"로 간주하는 것입니다. 객체 ID로 할 수있는 유일한 작업은 하나를 생성하고, 하나를 복사하고, 하나의 유형을 조회하거나 조작하거나, 둘이 같은지 비교하는 것입니다. object-ID로 다른 작업을 수행하려는 시도는 해당 ID가 참조하는 개체로 표시된 작업을 수행하는 약어로 간주됩니다.

참조 유형 인 Car 유형의 두 변수 X와 Y가 있다고 가정합니다. Y는 "개체 ID # 19531"을 보유합니다. "X = Y"라고 말하면 X는 "개체 ID # 19531"을 유지합니다. X도 Y도 차를 가지고 있지 않습니다. "개체 ID # 19531"이라고도하는 자동차는 다른 곳에 저장됩니다. Y를 X에 복사했을 때 ID 번호 만 복사했습니다. 이제 X.Color = Colors.Blue라고 가정하겠습니다. 이러한 진술은 "개체 ID # 19531"을 찾아 파란색으로 칠하라는 지시로 간주됩니다. X와 Y가 이제 노란색 자동차가 아닌 파란색 자동차를 참조하더라도이 명령문은 실제로 X 또는 Y에 영향을주지 않습니다. 둘 다 여전히 동일한 자동차 인 "개체 ID # 19531"을 참조하기 때문입니다. 항상 그랬습니다.


0

변수 유형 및 참조 값은 적용하기 쉽고 도메인 모델에 잘 적용되어 개발 프로세스를 용이하게합니다.

"값 유형"의 양에 대한 오해를 제거하기 위해 플랫폼에서 이것이 어떻게 처리되는지에 대해 설명하겠습니다. NET, 특히 C # (CSharp)에서 APIS를 호출하고 값, 참조, 메서드 및 함수에서 매개 변수를 전송하고 이러한 값의 전달을 올바르게 처리하는 방법

이 기사 읽기 C #의 변수 유형 값 및 참조


이것은 영어로만 제공되는 Q & A 사이트입니다. 불행히도 = \입니다. 그러나 답변 해 주셔서 감사합니다. 링크를 보조 용으로 만 사용하여 전체 답변을 작성하십시오 (완전한 지속 답변은 아님). 답변 방법을 살펴보십시오 .
Jesse

0

v값 유형 표현식 / 변수이고 r참조 유형 표현식 / 변수 라고 가정 합니다.

    x = v  
    update(v)  //x will not change value. x stores the old value of v

    x = r 
    update(r)  //x now refers to the updated r. x only stored a link to r, 
               //and r can change but the link to it doesn't .

따라서 값 유형 변수는 실제 값 (5 또는 "h")을 저장합니다. 참조 유형 변수는 값이있는 은유 적 상자에 대한 링크 만 저장합니다.


0

C #에서 사용할 수있는 다양한 데이터 유형을 설명하기 전에 C #이 강력한 유형의 언어라는 점을 언급하는 것이 중요합니다. 이는 각 변수, 상수, 입력 매개 변수, 반환 유형 및 일반적으로 값으로 평가되는 모든 표현식에 유형이 있음을 의미합니다.

각 유형에는 메모리를 할당하고 회수 할 때 유형 안전성을 보장하기 위해 CLR (공용 언어 런타임)에서 사용할 메타 데이터로 컴파일러가 실행 파일에 포함 할 정보가 포함되어 있습니다.

특정 유형이 할당하는 메모리 양을 알고 싶다면 다음과 같이 sizeof 연산자를 사용할 수 있습니다.

static void Main()
{
    var size = sizeof(int);
    Console.WriteLine($"int size:{size}");
    size = sizeof(bool);
    Console.WriteLine($"bool size:{size}");
    size = sizeof(double);
    Console.WriteLine($"double size:{size}");
    size = sizeof(char);
    Console.WriteLine($"char size:{size}");
}

출력은 각 변수에 의해 할당 된 바이트 수를 표시합니다.

int size:4
bool size:1
double size:8
char size:2

각 유형과 관련된 정보는 다음과 같습니다.

  • 필요한 저장 공간.
  • 최대 값과 최소값. 예를 들어 Int32 유형은 2147483648에서 2147483647 사이의 값을 허용합니다.
  • 상속 된 기본 유형입니다.
  • 런타임에 변수에 대한 메모리가 할당되는 위치입니다.
  • 허용되는 작업의 종류입니다.
  • 유형에 포함 된 구성원 (메서드, 필드, 이벤트 등)입니다. 예를 들어 int 유형의 정의를 확인하면 다음과 같은 구조체와 멤버를 찾을 수 있습니다.

    namespace System
    {
        [ComVisible(true)]
        public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
        {      
            public const Int32 MaxValue = 2147483647;     
            public const Int32 MinValue = -2147483648;
            public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);    
            ... 
        }  
    }

메모리 관리 운영 체제에서 여러 프로세스가 실행 중이고 RAM의 양이 모든 것을 수용하기에 충분하지 않은 경우 운영 체제는 하드 디스크의 일부를 RAM과 매핑하고 데이터를 하드 디스크에 저장하기 시작합니다. 운영 체제는 요청을 수행하기 위해 가상 주소가 해당 물리적 ​​주소에 매핑되는 특정 테이블보다 사용합니다. 이러한 메모리 관리 기능을 가상 메모리라고합니다.

각 프로세스에서 사용 가능한 가상 메모리는 다음 6 개 섹션으로 구성되어 있지만이 항목의 관련성을 위해 스택과 힙에만 초점을 맞출 것입니다.

스택 스택은 LIFO (last in, first out) 데이터 구조이며 운영 체제에 따라 크기가 달라집니다 (기본적으로 ARM, x86 및 x64 시스템의 경우 Windows의 예약 1MB, Linux 예약에 따라 2MB에서 8MB까지) 버전).

이 메모리 섹션은 CPU에 의해 자동으로 관리됩니다. 함수가 새 변수를 선언 할 때마다 컴파일러는 스택에 크기만큼 큰 새 메모리 블록을 할당하고 함수가 종료되면 변수의 메모리 블록이 할당 해제됩니다.

이 메모리 영역은 CPU에서 자동으로 관리되지 않으며 크기가 스택보다 큽니다. new 키워드가 호출되면 컴파일러는 요청 크기에 맞는 첫 번째 여유 메모리 블록을 찾기 시작합니다. 그리고 그것을 발견하면 내장 C 함수 malloc ()을 사용하여 예약 된 것으로 표시되고 해당 위치에 대한 포인터를 리턴합니다. 내장 된 C 함수 free ()를 사용하여 메모리 블록을 할당 해제 할 수도 있습니다. 이 메커니즘은 메모리 조각화를 일으키고 포인터를 사용하여 올바른 메모리 블록에 액세스해야합니다. 읽기 / 쓰기 작업을 수행하는 데 스택보다 느립니다.

사용자 지정 및 기본 제공 형식 C #은 정수, 부울, 텍스트 문자 등을 나타내는 표준 기본 제공 형식 집합을 제공하지만 struct, class, interface 및 enum과 같은 구문을 사용하여 고유 한 형식을 만들 수 있습니다.

struct 구문을 사용하는 사용자 정의 유형의 예는 다음과 같습니다.

struct Point
{
    public int X;
    public int Y;
};

값 및 참조 유형 C # 유형을 다음 범주로 분류 할 수 있습니다.

  • 값 유형
  • 참조 유형

값 형식 값 형식은 System.ValueType 클래스에서 파생되며이 형식의 변수에는 스택의 메모리 할당 내에 해당 값이 포함됩니다. 값 유형의 두 가지 범주는 struct와 enum입니다.

다음 예제는 boolean 유형의 멤버를 보여줍니다. 보시다시피 System.ValueType 클래스에 대한 명시적인 참조가 없기 때문에이 클래스는 구조체에 의해 상속되기 때문에 발생합니다.

namespace System
{
    [ComVisible(true)]
    public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
    {
        public static readonly string TrueString;
        public static readonly string FalseString;
        public static Boolean Parse(string value);
        ...
    }
}

참조 유형 반면에 참조 유형에는 변수에 저장된 실제 데이터가 포함되지 않고 값이 저장된 힙의 메모리 주소가 포함됩니다. 참조 유형의 범주는 클래스, 대리자, 배열 및 인터페이스입니다.

런타임에 참조 유형 변수가 선언되면 new 키워드를 사용하여 생성 된 개체가 할당 될 때까지 null 값이 포함됩니다.

다음 예제는 일반 유형 List의 멤버를 보여줍니다.

namespace System.Collections.Generic
{
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
    [DefaultMember("Item")]
    public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        ...
        public T this[int index] { get; set; }
        public int Count { get; }
        public int Capacity { get; set; }
        public void Add(T item);
        public void AddRange(IEnumerable<T> collection);
        ...
    }
}

특정 개체의 메모리 주소를 찾으려는 경우 System.Runtime.InteropServices 클래스는 관리되지 않는 메모리에서 관리되는 개체에 액세스하는 방법을 제공합니다. 다음 예제에서는 정적 메서드 GCHandle.Alloc ()을 사용하여 핸들을 문자열에 할당 한 다음 AddrOfPinnedObject 메서드를 사용하여 주소를 검색합니다.

string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");

출력은

Memory address:39723832

참조 공식 문서 : https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019


-1

표준에 의해 명시 적으로 명시된 값 유형과 참조 유형 간의 차이점에 대한 세부 사항이 많으며 그중 일부는 특히 초보자에게 이해하기 쉽지 않습니다.

ECMA 표준 33, 공용 언어 인프라 (CLI)를 참조하십시오 . CLI는 ISO에 의해 표준화되었습니다. 참조를 제공하지만 ECMA의 경우 PDF를 다운로드해야하며 해당 링크는 버전 번호에 따라 다릅니다. ISO 표준은 비용이 듭니다.

한 가지 차이점은 값 유형은 boxing 할 수 있지만 참조 유형은 일반적으로 할 수 없다는 것입니다. 예외가 있지만 상당히 기술적입니다.

값 유형은 매개 변수없는 인스턴스 생성자 또는 종료자를 가질 수 없으며 자신을 참조 할 수 없습니다. 자체 참조하면 값 유형이 있는지 것을 예 수단 노드 다음의 멤버 노드가 수없는 노드 . 사양에 다른 요구 사항 / 제한 사항이 있다고 생각하지만 그렇다면 한곳에 모이지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.