.NET에서 구조체와 클래스의 차이점은 무엇입니까?


답변:


1058

.NET에는 두 가지 범주 유형 ( 참조 유형값 유형)이 있습니다. .

구조는 값 유형 이고 클래스는 참조 유형입니다 입니다.

일반적인 차이점은 참조 유형 이 힙에 있고 값 유형이 있다는 것입니다. 인라인으로 유지된다는 것입니다. 즉 변수 또는 필드가 정의 된 위치입니다.

값 유형을 포함 하는 변수 는 전체 값 유형을 포함합니다 값이 포함됩니다. 구조체의 경우 변수에 모든 필드와 함께 구조체 전체가 포함됩니다.

참조 유형을 포함하는 변수 는 포인터 또는 참조를 포함합니다 실제 값이있는 메모리의 다른 곳에 됩니다.

여기에는 다음과 같은 장점이 있습니다.

  • 값 유형 에는 항상 포함됩니다
  • 참조 유형 에는 null 참조 가 포함될 수 있습니다. 즉, 현재 아무 것도 참조하지 않습니다.

내부적으로 참조 유형 은 포인터로 구현되며 변수 할당의 작동 방식을 알고 다른 행동 패턴이 있습니다.

  • 값 유형 변수 의 내용 을 다른 변수에 복사하면 전체 내용을 새 변수에 복사하여 두 가지를 구별합니다. 다시 말해, 복사 후에 하나의 변경 사항이 다른 하나의 변경 사항에 영향을 미치지 않습니다.
  • 참조 유형 변수 의 내용 을 다른 변수에 복사하면 참조가 복사 됩니다. 즉, 실제 데이터를 저장하는 다른 곳에 동일한 두 개의 참조가 있습니다. 다시 말해, 복사 후에 한 참조에서 데이터를 변경하면 다른 참조에도 영향을주는 것처럼 보이지만 실제로는 두 곳에서 동일한 데이터를보고 있기 때문입니다.

변수 또는 필드를 선언 할 때 두 유형의 차이점은 다음과 같습니다.

  • 변수 : 값 유형 은 스택에 있고, 참조 유형 은 실제 메모리가있는 힙 메모리의 어딘가에 대한 포인터로 스택에 있습니다 ( Eric Lipperts 기사 시리즈 : 스택은 구현 세부 사항 입니다).
  • class / struct-field : 값 유형 은 유형 내부에 완전히 존재하고 참조 유형 은 실제 메모리가 존재하는 힙 메모리의 어딘가에 대한 포인터로 유형 내부에 있습니다.

43
완전한 완성을 위해 Eric Lippert가 스택이 구현 세부 사항 이라고 말했음을 언급해야합니다. 위의 스택을 언급 할 때마다 Eric의 게시물을 염두에 두십시오.
Lasse V. Karlsen 2016 년

2
이것이 모두 C ++에도 유효합니까?
Koray Tugay

9
또 다른 중요한 차이점은 사용법입니다. MSDN의 "구조는 일반적으로 사각형 좌표와 같은 작은 관련 변수 그룹을 캡슐화하는 데 사용됩니다. Structs에는 생성자, 상수, 필드, 메서드, 속성, 인덱서, 연산자, 이벤트 및 중첩 유형도 포함될 수 있습니다. 멤버가 필요합니다. 대신 유형을 클래스로 만드는 것을 고려해야합니다. "
thewpfguy

4
@KorayTugay 아닙니다.
ZoomIn

9
C ++ 구조체와 클래스의 @KorayTugay는 기본 액세스 제한 (클래스는 기본적으로 프라이빗, 구조체는 공용)을 제외하고는 완전히 동일합니다.
berkus

207

각각에 대한 간단한 요약 :

수업 만 :

  • 상속을 지원할 수있다
  • 참조 (포인터) 유형
  • 참조는 null 일 수 있습니다
  • 새 인스턴스 당 메모리 오버 헤드가 있음

구조 만 :

  • 상속을 지원할 수 없습니다
  • 가치 유형
  • 정수와 같은 값으로 전달
  • Nullable을 사용하지 않는 한 null 참조를 가질 수 없습니다.
  • 'boxed'가 아닌 한 새 인스턴스 당 메모리 오버 헤드가 없습니다.

클래스와 구조 :

  • 복합 데이터 유형은 일반적으로 논리적 관계가있는 몇 가지 변수를 포함하는 데 사용됩니까?
  • 메소드와 이벤트를 포함 할 수 있습니다
  • 인터페이스 지원 가능

16
이 답변에는 옳지 않은 부분이 있습니다. 클래스가 항상 힙에있는 것은 아니며 구조체가 항상 스택에있는 것은 아닙니다. 현재 예외에는 클래스의 구조체 필드, 익명 메서드 및 람다 식으로 캡처 된 변수, 반복자 블록 및 이미 언급 된 상자 값이 포함됩니다. 그러나 스택 대 힙 할당은 구현 세부 사항이며 변경 될 수 있습니다. 에릭 립 파트가 이에 대해 논의합니다 . downvoted했지만 업데이트하면 행복하게 제거합니다.
Simon P Stevens

1
struct는 다른 클래스 / 클래스로부터의 상속을 지원하지 않지만 struct에서 인터페이스를 구현할 수 있습니다.
thewpfguy

2
"새 인스턴스 당 메모리 오버 헤드가 없다"는 구조 를 주장 할 때 의미하는 바를 명확하게 설명 할 수 있습니다 . 내 첫 해석은 구조체가 제로 메모리를 사용한다고 주장하는 것입니다. 그런 다음 클래스와 달리 구조체가 멤버 필드의 합계만큼 많은 메모리를 필요로한다고 말하려고한다고 생각했을 수도 있습니다. 그러나 나는 구글 스에서 Hans Passant의 대답c# struct memory overhead찾았습니다 . 그렇지 않다. 무슨 소리 ?
Mark Amery 23

4
@MarkAmery 나는 "메모리 오버 헤드 없음"이라는 표현과 동일하게 초기 반응을 보였지만 OP는 인스턴스 class가 관리되는 메모리 (가비지 수집기에서 처리됨) 라는 사실을 언급하고 있다고 생각 하지만 인스턴스 struct는 그렇지 않습니다. .
Hutch

1
"정수는 값과 같은 값으로 전달됩니다"는 false입니다. 모든 변수는 값과 참조 유형으로 전달됩니다. 참조로 변수를 전달하려면 "ref"키워드를 사용해야합니다. jonskeet.uk/csharp/parameters.html#ref
Marco

41

.NET에서 구조체와 클래스 선언은 참조 형식과 값 형식을 구분합니다.

참조 유형을 전달하면 실제로 저장된 유형이 하나뿐입니다. 인스턴스에 액세스하는 모든 코드가 동일한 코드에 액세스하고 있습니다.

값 유형을 전달하면 각 값이 사본입니다. 모든 코드는 자체 사본에서 작동합니다.

이것은 예제와 함께 표시 될 수 있습니다.

struct MyStruct 
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

수업의 경우 이것은 다릅니다

class MyClass 
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

클래스는 아무것도 될 수 없습니다-참조는 null을 가리킬 수 있습니다.

Structs는 실제 값입니다. 비어있을 수 있지만 null이 될 수 없습니다. 이러한 이유로 구조체에는 항상 매개 변수가없는 기본 생성자가 있습니다. '시작 값'이 필요합니다.


@ T.Todua 네, 위의 더 나은 답변이 있습니다.이 답변을 제공 한 후에 투표하고 답변으로 선택했습니다.
Keith

1
나는 당신이 나를 올바르게 이해했는지 모르겠습니다. 나는 위의 대답과 반대되는 당신의 대답을 정말로 찬성 / 수락했습니다. ).
T.Todua

24

구조체와 클래스의 차이점 :

  • Structs는 값 형식 이지만 Class는 참조 형식입니다 입니다.
  • 구조체는 스택 에 저장 되는 반면 클래스는 힙에 저장됩니다. .
  • 값 유형은 선언 된 메모리에 값을 보유하지만 참조 유형은 오브젝트 메모리에 대한 참조를 보유합니다.
  • 범위가 손실 된 직후 값 유형이 삭제 되는 반면 범위가 손실 된 후 변수 유형 만 파괴됩니다. 객체는 나중에 가비지 수집기에 의해 파괴됩니다.
  • 구조체를 다른 구조체에 복사 할 때 한 구조체에서 수정 된 새 구조체의 구조체는 다른 구조체의 값에 영향을 미치지 않습니다.
  • 클래스를 다른 클래스에 복사하면 참조 변수 만 복사됩니다.
  • 참조 변수는 모두 힙에서 동일한 객체를 가리 킵니다. 한 변수로 변경하면 다른 참조 변수에 영향을줍니다.
  • 구조물은 소멸자를 가질 수 없습니다 를 가질 수 없지만 클래스에는 소멸자를 가질 수 있습니다.
  • 클래스는 구조체가 상속을 지원하지 않지만 클래스는 구조체를 지원하지 않지만 구조체는 명시적인 매개 변수가없는 생성자를 가질 수 없습니다 . 둘 다 인터페이스에서 상속을 지원합니다.
  • Structs는 밀폐형입니다 .

21

Microsoft의 클래스와 구조 선택 중 ...

경험상 프레임 워크에서 대부분의 유형은 클래스 여야합니다. 그러나 값 유형의 특성으로 인해 구조체를 사용하는 것이 더 적합한 상황이 있습니다.

클래스 대신 구조체고려하십시오 .

  • 유형의 인스턴스가 작고 일반적으로 수명이 짧거나 다른 개체에 일반적으로 포함 된 경우

X 유형 에 다음 특성 이 모두 없는 경우 구조체 를 피하십시오.

  • 기본 유형 (int, double 등)과 유사한 단일 값을 논리적으로 나타냅니다.
  • 인스턴스 크기는 16 바이트 미만입니다.
  • 불변입니다. (변경 불가)
  • 자주 박스에 넣을 필요는 없습니다.

19

다른 답변에 설명 된 모든 차이점 외에도 :

  1. Structs 는 명시적인 매개 변수없는 생성자를 가질 수 없지만 클래스는
  2. Structs 는 소멸자를 가질 수 없습니다 클래스는
  3. 구조체 다른 구조체 나 클래스에서 상속 할 수 없지만 클래스는 다른 클래스에서 상속 할 수 있습니다. (구조와 클래스 모두 인터페이스에서 구현할 수 있습니다.)

모든 차이점을 설명하는 비디오를 시청 한 경우 C # 에서 Part 29-C # Tutorial-클래스와 구조체 의 차이점을 확인할 수 있습니다 .


4
.net 언어가 일반적으로 구조체가 매개 변수가없는 생성자를 정의 할 수 없다는 사실보다 훨씬 더 중요합니다 (언어 컴파일러에 의한 결정 여부 결정)는 구조가 존재하고 노출 될 수 있다는 사실입니다. 매개 변수가없는 생성자가 정의 된 경우에도 생성자가 실행되지 않고 외부에 .net 언어가 일반적으로 구조체에 대한 매개 변수가없는 생성자를 금지하는 이유는 이러한 생성자가 때때로 실행되고 때로는 실행되지 않는 결과로 인한 혼란을 피하기 위해서입니다.
supercat

15

클래스 인스턴스는 관리되는 힙에 저장됩니다. 인스턴스를 포함하는 모든 변수는 단순히 힙의 인스턴스에 대한 참조입니다. 메소드에 오브젝트를 전달하면 오브젝트 자체가 아니라 참조 사본이 전달됩니다.

구조 (기술적으로 가치 유형)는 기본 유형과 매우 유사하게 사용되는 모든 위치에 저장됩니다. 내용은 사용자 정의 된 복사 생성자를 호출하지 않고 언제든지 런타임에 의해 복사 될 수 있습니다. 메소드에 값 유형을 전달하려면 사용자 정의 가능한 코드를 호출하지 않고 전체 값을 다시 복사해야합니다.

C ++ / CLI 이름으로 구별이 더 잘됩니다. "ref class"는 먼저 설명 된 클래스이고 "value class"는 두 번째로 설명 된 클래스입니다. C #에서 사용하는 "class"및 "struct"키워드는 반드시 알아야합니다.


11
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                        |                                                Struct                                                |                                               Class                                               |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type                   | Value-type                                                                                           | Reference-type                                                                                    |
| Where                  | On stack / Inline in containing type                                                                 | On Heap                                                                                           |
| Deallocation           | Stack unwinds / containing type gets deallocated                                                     | Garbage Collected                                                                                 |
| Arrays                 | Inline, elements are the actual instances of the value type                                          | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost             | Cheap allocation-deallocation                                                                        | Expensive allocation-deallocation                                                                 |
| Memory usage           | Boxed when cast to a reference type or one of the interfaces they implement,                         | No boxing-unboxing                                                                                |
|                        | Unboxed when cast back to value type                                                                 |                                                                                                   |
|                        | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |                                                                                                   |
| Assignments            | Copy entire data                                                                                     | Copy the reference                                                                                |
| Change to an instance  | Does not affect any of its copies                                                                    | Affect all references pointing to the instance                                                    |
| Mutability             | Should be immutable                                                                                  | Mutable                                                                                           |
| Population             | In some situations                                                                                   | Majority of types in a framework should be classes                                                |
| Lifetime               | Short-lived                                                                                          | Long-lived                                                                                        |
| Destructor             | Cannot have                                                                                          | Can have                                                                                          |
| Inheritance            | Only from an interface                                                                               | Full support                                                                                      |
| Polymorphism           | No                                                                                                   | Yes                                                                                               |
| Sealed                 | Yes                                                                                                  | When have sealed keyword                                                                          |
| Constructor            | Can not have explicit parameterless constructors                                                     | Any constructor                                                                                   |
| Null-assignments       | When marked with nullable question mark                                                              | Yes (+ When marked with nullable question mark in C# 8+)                                          |
| Abstract               | No                                                                                                   | When have abstract keyword                                                                        |
| Member Access Modifiers| public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

1
이것은 실제로 매우 훌륭합니다. 요약되고 유익합니다. 적어도 한 번만 답을 교정해야 함을 기억하십시오-구조체와 클래스 설명을 일부 행으로 바꾸었고 오타가 있습니다.
Robert Synoradzki

1
@ensisNoctis 그 실수와 편집에 감사드립니다. 나는 😅 내 대답을 읽기 다시해야
0xaryan

8

구조 대 클래스

구조체는 값 형식이므로 스택에 저장되지만 클래스는 참조 형식이며 힙에 저장됩니다.

구조는 상속과 다형성을 지원하지 않지만 클래스는 둘 다를 지원합니다.

기본적으로 모든 구조체 멤버는 공개이지만 클래스 멤버는 기본적으로 비공개입니다.

구조체는 값 형식이므로 구조체 개체에 null을 할당 할 수 없지만 클래스에는 해당되지 않습니다.


5
"모든 구조체 멤버는 공개입니다"에 관하여 : 내가 실수하지 않으면, 그것은 맞지 않습니다. "중첩 클래스 및 구조체를 포함한 클래스 멤버 및 구조체 멤버의 액세스 수준은 기본적으로 비공개입니다." msdn.microsoft.com/ko-kr/library/ms173121.aspx
Nate Cook

8

다른 답변에 덧붙이 자면 주목할 가치가있는 근본적인 차이점이 있습니다. 즉, 성능에 큰 영향을 줄 수 있기 때문에 데이터가 배열 내에 저장되는 방식입니다.

  • 구조체를 사용하면 배열에 구조체의 인스턴스가 포함됩니다
  • 클래스를 사용하면 배열은 메모리의 다른 곳에서 클래스의 인스턴스에 대한 포인터를 포함합니다

따라서 구조체 배열은 메모리에서 이와 같이 보입니다.

[struct][struct][struct][struct][struct][struct][struct][struct]

클래스 배열은 다음과 같습니다.

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

클래스 배열을 사용하면 관심있는 값이 배열 내에 저장되지 않고 메모리의 다른 곳에 저장됩니다.

대부분의 응용 프로그램에서이 차이는 실제로 중요하지 않지만, 고성능 코드에서 이는 메모리 내의 데이터의 지역성에 영향을 미치며 CPU 캐시의 성능에 큰 영향을 미칩니다. 구조체를 사용할 수 있거나 사용해야 할 때 클래스를 사용하면 CPU에서 캐시 누락 수가 크게 증가합니다.

현대 CPU가 가장 느린 것은 숫자를 처리하지 않고 메모리에서 데이터를 가져 오는 것입니다 .L1 캐시 적중은 RAM에서 데이터를 읽는 것보다 몇 배 빠릅니다.

테스트 할 수있는 코드가 있습니다. 내 컴퓨터에서 클래스 배열을 반복하면 구조체 배열보다 ~ 3 배 더 오래 걸립니다.

    private struct PerformanceStruct
    {
        public int i1;
        public int i2;
    }

    private class PerformanceClass
    {
        public int i1;
        public int i2;
    }

    private static void DoTest()
    {
        var structArray = new PerformanceStruct[100000000];
        var classArray = new PerformanceClass[structArray.Length];

        for (var i = 0; i < structArray.Length; i++)
        {
            structArray[i] = new PerformanceStruct();
            classArray[i] = new PerformanceClass();
        }

        long total = 0;
        var sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < structArray.Length; i++)
        {
            total += structArray[i].i1 + structArray[i].i2;
        }

        sw.Stop();
        Console.WriteLine($"Struct Time: {sw.ElapsedMilliseconds}");
        sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < classArray.Length; i++)
        {
            total += classArray[i].i1 + classArray[i].i2;
        }

        Console.WriteLine($"Class Time: {sw.ElapsedMilliseconds}");
    }

-1; "구조는 값 유형이므로 값을 저장하고 클래스는 참조 유형이므로 클래스를 참조합니다." " 여기서 다른 클래스의 대답에서 그것을 이해하지 못한 사람에게는 명확하지 않으며 이해가되지 않을 것입니다." 클래스를 클래스 인스턴스와 혼동합니다.
Mark Amery

@MarkAmery 나는 약간 명확하게하려고 노력했다. 필자가 실제로 시도한 요점은 배열이 값 및 참조 유형으로 작동하는 방식과 이것이 성능에 미치는 영향의 차이였습니다. 나는 이것이 다른 많은 답변에서 수행되는 것처럼 어떤 가치와 참조 유형이 무엇인지 다시 설명하려고하지 않았습니다.
Will Calderwood

7

그것을 완성하기 위해, 사용시 또 다른 차이점이 있습니다. Equals 모든 클래스와 구조에 상속되는 메소드를 이 있습니다.

클래스와 구조가 있다고 가정 해 봅시다.

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

Main 메소드에는 4 개의 객체가 있습니다.

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

그때:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

따라서 구조는 점 (x 및 y 좌표 저장)과 같은 숫자 형 객체에 적합합니다. 그리고 수업은 다른 사람들에게 적합합니다. 두 사람의 이름, 키, 몸무게가 동일하더라도 여전히 두 사람입니다.


6

글쎄, 우선, 구조체는 참조가 아닌 값으로 전달됩니다. 구조는 비교적 간단한 데이터 구조에 적합하지만 클래스는 다형성 및 상속을 통해 아키텍처 관점에서 훨씬 더 유연합니다.

다른 사람들은 아마도 나보다 더 자세하게 설명 할 수 있지만, 내가 가고있는 구조가 단순 할 때 구조체를 사용합니다.


4

액세스 지정자의 기본 차이점과 위에서 언급 한 몇 가지 외에도 출력이있는 코드 샘플과 함께 위에서 언급 한 것을 포함하여 주요 차이점을 추가하고 싶습니다. 이는 참조 및 값에 대한 명확한 아이디어를 제공합니다.

구조 :

  • 값 유형이며 힙 할당이 필요하지 않습니다.
  • 메모리 할당이 다르고 스택에 저장됩니다
  • 작은 데이터 구조에 유용
  • 성능에 영향을 미치면 값을 메소드에 전달할 때 전체 데이터 구조를 전달하고 모두가 스택으로 전달됩니다.
  • 생성자는 구조체 값 자체를 (일반적으로 스택의 임시 위치에) 반환하고 필요한 경우이 값을 복사합니다
  • 변수에는 각각 고유 한 데이터 사본이 있으며 한 조작이 다른 조작에 영향을 줄 수 없습니다.
  • 사용자 지정 상속을 지원하지 않으며 유형 객체에서 암시 적으로 상속합니다.

수업:

  • 참조 유형 값
  • 힙에 저장
  • 동적으로 할당 된 객체에 대한 참조 저장
  • 생성자는 new 연산자를 사용하여 호출되지만 힙에 메모리를 할당하지는 않습니다.
  • 여러 변수가 동일한 객체에 대한 참조를 가질 수 있습니다
  • 한 변수에 대한 조작이 다른 변수가 참조하는 오브젝트에 영향을 줄 수 있습니다

코드 샘플

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();

        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

산출

Struct Object의 초기 값은 10입니다.

Struct Object의 Inside Method 값은 다음과 같습니다. 20

Struct Object의 Method 호출 후 값은 다음과 같습니다. 10

클래스 객체의 초기 값은 : 10

내부 클래스 메서드 클래스 개체의 내부 메서드 값은 다음과 같습니다. 20

클래스 오브젝트의 메소드 호출 후 값은 다음과 같습니다. 20

여기에서 값별 호출과 참조 별 호출의 차이점을 명확하게 확인할 수 있습니다.


4
  1. 클래스에 선언 된 이벤트는 잠금을 통해 + = 및-= 액세스가 자동으로 잠겨있어 스레드를 안전하게 만듭니다 (정적 이벤트는 클래스 유형에 잠겨 있습니다). 구조체에 선언 된 이벤트에는 + = 및-= 액세스가 자동으로 잠기지 않습니다. 참조 유형 표현식에서만 잠글 수 있으므로 구조체의 잠금 (this)이 작동하지 않습니다.

  2. struct 인스턴스를 작성하면 가비지 콜렉션이 발생할 수 없으며 (생성자가 직접 또는 간접적으로 참조 유형 인스턴스를 작성하지 않는 한) 참조 유형 인스턴스를 작성하면 가비지 콜렉션이 발생할 수 있습니다.

  3. 구조체에는 항상 기본 제공 공용 기본 생성자가 있습니다.

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }

    이것은 구조체가 항상 인스턴스화 가능하지만 클래스는 모든 생성자가 비공개 일 수 있으므로 클래스가 아닐 수도 있음을 의미합니다.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
  4. 구조체는 소멸자를 가질 수 없습니다. 소멸자는 객체의 재정의 일뿐입니다. 변장으로 마무리하고 값 형식 인 구조체는 가비지 수집 대상이 아닙니다.

    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }
    
    And the CIL for ~Indirect() looks like this:
    
    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
  5. 구조체는 암시 적으로 봉인되어 있으며 클래스는 그렇지 않습니다.
    구조체는 추상적 일 수없고 클래스는 할 수 없습니다.
    구조체는 생성자에서 base ()를 호출 할 수 없지만 명시적인 기본 클래스가없는 클래스는 호출 할 수 없습니다.
    구조체는 다른 클래스를 확장 할 수 없으며 클래스는 확장 할 수 있습니다.
    구조체는 클래스가 보호 할 수있는 멤버 (예 : 필드, 중첩 형식)를 선언 할 수 없습니다.
    구조체는 추상 함수 멤버를 선언 할 수 없으며 추상 클래스는 선언 할 수 있습니다.
    구조체는 가상 함수 멤버를 선언 할 수 없으며 클래스는 선언 할 수 있습니다.
    구조체는 봉인 된 함수 멤버를 선언 할 수 없으며 클래스는 선언 할 수 있습니다. 이 규칙의 한 가지 예외는 구조체가 System.Object, viz, Equals () 및 GetHashCode () 및 ToString ()의 가상 메서드를 재정의 할 수 있다는 것입니다.
    구조체는 재정의 함수 멤버를 선언 할 수 없으며 클래스는 선언 할 수 있습니다.


어떤 상황에서 구조체와 함께 이벤트를 사용합니까? 매우 신중하게 작성된 프로그램이 구조체와 함께 이벤트를 작동하는 방식으로 사용할 수 있지만 구조체가 값으로 복사되거나 전달되지 않은 경우에만 클래스가 될 수 있다고 상상할 수 있습니다.
supercat December

@supercat 예, 구조체의 정적이 아닌 이벤트는 매우 이상하며 변경 가능한 구조체에만 유용하며 이벤트 자체 ( "필드와 같은"이벤트 인 경우)는 구조체를 "mutable"로 바꿉니다. "범주와 구조체에 참조 유형의 필드를 소개합니다. 구조체의 비 정적 이벤트는 사악해야합니다.
Jeppe Stig Nielsen

@JeppeStigNielsen : 구조체가 이벤트를 갖는 것이 어디에서 의미가 있는지 알 수있는 유일한 패턴은 구조체의 목적이 프록시로 동작하는 클래스 객체에 대한 불변의 참조를 보유하는 것입니다. 그러나 자동 시나리오는 이러한 시나리오에서 전혀 쓸모가 없습니다. 대신, 구독 및 구독 취소 이벤트는 구조 뒤의 클래스로 릴레이되어야합니다. .NET이 초기에 널 (null)로 숨겨진 숨겨진 필드 유형을 가진 '캐시 박스'구조 유형을 갖고 있거나 정의 할 수 있기 Object를 희망합니다.이 박스는 구조체의 박스 사본에 대한 참조를 보유합니다.
supercat

1
@JeppeStigNielsen : Structs는 많은 프록시 사용 시나리오에서 클래스를 능가합니다. 구조체를 사용할 때 가장 큰 문제는 권투가 필요한 경우 종종 내부 루프로 연기된다는 것입니다. 구조가 반복적으로 박스 화 되는 것을 피할 수있는 방법 이 있다면 더 많은 사용 시나리오에서 클래스보다 낫습니다.
supercat

4

앞에서 언급 한 바와 같이 : 클래스는 참조 유형이고 Structs는 모든 결과를 갖는 값 유형입니다.

일반적으로 Framework Design Guidelines는 다음과 같은 경우 클래스 대신 Structs를 사용하도록 권장합니다.

  • 인스턴스 크기가 16 바이트 미만입니다.
  • 기본 유형 (int, double 등)과 유사한 논리적으로 단일 값을 나타냅니다.
  • 불변입니다
  • 자주 박스에 넣을 필요는 없습니다

3

"class vs struct"퍼즐의 흥미로운 사례가 하나 있습니다. 메소드에서 몇 가지 결과를 반환해야 할 경우 : 사용할 것을 선택하십시오. ValueTuple 스토리를 아는 경우-TuTu (클래스)보다 ValueTuple (struct)이 더 효과적이기 때문에 ValueTuple (struct)이 추가 된 것을 알 수 있습니다. 그러나 숫자의 의미는 무엇입니까? 두 가지 테스트 : 하나는 2 개의 필드가있는 구조체 / 클래스이고 다른 하나는 8 개의 필드가있는 구조체 / 클래스입니다 (차원이 4 이상-클래스는 프로세서 틱 측면에서보다 효과적이어야하지만 물론 GC로드도 고려해야합니다) ).

추신 : 특정 사례 '견고한 클래스 또는 클래스'에 대한 또 다른 벤치 마크는 다음과 같습니다. https://stackoverflow.com/a/45276657/506147

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

코드 테스트 :

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;

namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }


        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }

        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }

        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}

2

Structs는 실제 값입니다. 비어있을 수 있지만 결코 null이 아닙니다.

그러나 .NET 2 structs는 Nullable 버전을 지원하고 C #은 사용하기 쉽도록 일부 구문 설탕을 제공합니다.

int? value = null;
value  = 1;

1
이것은 'Nullable <int> value = null;'을 읽는 구문 설탕 일뿐입니다.
Erik van Brakel

@ErikvanBrakel 그것은 단지 구문 설탕이 아닙니다. 다른 복싱 규칙은 (object)(default(int?)) == null설탕 이외의 다른 값 유형으로는 할 수없는 것을 의미 합니다. 유일한 설탕은 int?입니다 Nullable<int>.
Jon Hanna

-1; 이것은 구조체와 클래스의 차이점이 무엇인지에 대한 질문을 다루지 않으므로 별도의 답변이 아니라 답장에 대한 답변이어야합니다. (아마도 사이트 규범은 2008 년 8 월에 달라졌지만!)
Mark Amery

1

기본 값 유형 또는 구조 유형의 모든 변수 또는 필드에는 모든 필드 (공용 및 개인)를 포함하여 해당 유형의 고유 한 인스턴스가 있습니다. 대조적으로, 참조 유형의 변수 또는 필드는 널 (null)을 보유 할 수 있거나, 다른 수의 다른 참조가 존재할 수도있는 다른 곳에 저장된 오브젝트를 나타낼 수 있습니다. 구조체의 필드는 스택에 있거나 다른 힙 객체의 일부일 수있는 해당 구조 유형의 변수 또는 필드와 같은 위치에 저장됩니다 .

기본 값 유형의 변수 또는 필드를 작성하면 기본값으로 작성됩니다. 구조 유형의 변수 또는 필드를 작성하면 새 인스턴스가 작성되어 기본 방식으로 모든 필드가 작성됩니다. 참조 유형 의 새 인스턴스 작성 은 기본 방식으로 모든 필드를 작성한 다음 유형에 따라 선택적 추가 코드를 실행하여 시작됩니다.

프리미티브 유형의 한 변수 또는 필드를 다른 유형으로 복사하면 값이 복사됩니다. 하나의 변수 또는 구조 유형의 필드를 다른 유형으로 복사하면 이전 인스턴스의 모든 필드 (공개 및 개인)가 후자의 인스턴스로 복사됩니다. 하나의 변수 또는 참조 유형의 필드를 다른 유형으로 복사하면 후자가 이전과 동일한 인스턴스를 참조합니다 (있는 경우).

C ++과 같은 일부 언어에서 형식의 의미 적 동작은 형식의 저장 방식과 무관하지만 .NET에는 해당되지 않습니다. 유형이 변경 가능한 값 의미를 구현하는 경우 해당 유형의 한 변수를 다른 유형으로 복사하면 첫 번째의 특성이 두 번째 인스턴스에 의해 참조되는 다른 인스턴스로 복사되고 두 번째의 멤버를 사용하여 변경하면 두 번째 인스턴스가 변경됩니다. 하지만 처음은 아닙니다. 유형이 변경 가능한 참조 의미를 구현하는 경우 한 변수를 다른 변수에 복사하고 두 번째 멤버를 사용하여 오브젝트를 변경하면 첫 번째 변수가 참조하는 오브젝트에 영향을 미칩니다. 불변의 의미를 가진 타입은 돌연변이를 허용하지 않기 때문에, 복사가 새로운 인스턴스를 생성하는지 또는 첫 번째에 대한 다른 참조를 생성하는지는 의미 적으로 중요하지 않습니다.

.NET에서는 모든 필드가 동일하게 수행 될 수있는 경우 값 유형이 위의 의미를 구현할 수 있습니다. 그러나 참조 유형은 변경 가능한 참조 시맨틱 또는 변경 불가능한 시맨틱 만 구현할 수 있습니다. 변경 가능한 참조 유형의 필드가있는 값 유형은 변경 가능한 참조 시맨틱 또는 이상한 하이브리드 시맨틱을 구현하는 것으로 제한됩니다.

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