.NET에서 구조체와 클래스의 차이점은 무엇입니까?
.NET에서 구조체와 클래스의 차이점은 무엇입니까?
.NET에는 참조 유형 과 값 유형 의 두 가지 유형 범주가 있습니다 .
구조체는 값 유형 이고 클래스는 참조 유형 입니다.
일반적인 차이점은 참조 유형 이 힙에 있고 값 유형이 인라인으로 존재한다는 것입니다. 즉, 변수 또는 필드가 정의 된 모든 위치에 있습니다.
값 유형을 포함 하는 변수 는 전체 값 유형 값을 포함합니다 . 구조체의 경우 이는 변수에 모든 필드와 함께 전체 구조체가 포함되어 있음을 의미합니다.
참조 유형을 포함하는 변수 에는 실제 값이있는 메모리의 다른 위치에 대한 포인터 또는 참조가 포함 됩니다.
여기에는 다음과 같은 한 가지 이점이 있습니다.
- 값 유형 에는 항상 값이 포함됩니다.
- 참조 유형 은 null 참조 를 포함 할 수 있습니다. 즉, 지금은 아무것도 참조하지 않습니다.
내부적으로 참조 유형 은 포인터로 구현되며 변수 할당이 작동하는 방식을 알고 있으면 다른 동작 패턴이 있습니다.
- 값 유형 변수 의 내용 을 다른 변수에 복사하면 전체 내용이 새 변수에 복사되어 두 가지가 구별됩니다. 즉, 복사 후 하나를 변경해도 다른 하나에 영향을주지 않습니다.
- 참조 유형 변수 의 내용 을 다른 변수에 복사하면 참조가 복사됩니다. 즉, 이제 실제 데이터의 다른 저장 위치 에 동일한 참조가 두 개 있습니다. 즉, 복사 후 한 참조의 데이터를 변경하면 다른 참조에도 영향을 미치는 것처럼 보이지만 실제로는 두 위치에서 동일한 데이터를보고 있기 때문입니다.
변수 또는 필드를 선언 할 때 두 유형의 차이점은 다음과 같습니다.
- 변수 : 값 유형 은 스택에 있고 참조 유형 은 실제 메모리가있는 힙 메모리의 어딘가에 대한 포인터로 스택에 있습니다 ( Eric Lipperts 기사 시리즈 : The Stack Is An Implementation Detail 참고) .
- class / struct-field : 값 유형 은 완전히 유형 내부에 있으며 참조 유형 은 실제 메모리가있는 힙 메모리의 어딘가에 대한 포인터로 유형 내부에 있습니다.
각각에 대한 간략한 요약 :
수업 만 :
- 상속 지원 가능
- 참조 (포인터) 유형
- 참조는 null 일 수 있습니다.
- 새 인스턴스 당 메모리 오버 헤드가 있습니다.
구조체 만 :
- 상속을 지원할 수 없습니다.
- 가치 유형
- 값으로 전달됨 (예 : 정수)
- 널 참조를 가질 수 없습니다 (Nullable이 사용되지 않는 한).
- 새 인스턴스 당 메모리 오버 헤드 없음- '박스형'이 아닌 경우
클래스와 구조체 모두 :
- 논리적 관계가있는 몇 가지 변수를 포함하는 데 일반적으로 사용되는 복합 데이터 유형입니다.
- 메서드 및 이벤트를 포함 할 수 있습니다.
- 인터페이스 지원 가능
.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을 가리킬 수 있습니다.
구조체는 실제 값입니다. 비어있을 수 있지만 null 일 수는 없습니다. 이러한 이유로 구조체에는 항상 매개 변수가없는 기본 생성자가 있습니다. '시작 값'이 필요합니다.
구조체와 클래스의 차이점 :
- 구조체는 값 유형 이고 클래스는 참조 유형 입니다.
- 구조체는 스택 에 저장 되는 반면 클래스는 힙에 저장됩니다 .
- 값 유형은 선언 된 메모리에 해당 값을 보유하지만 참조 유형은 객체 메모리에 대한 참조를 보유합니다.
- 값 유형 은 범위가 손실 된 후 즉시 삭제 되는 반면 참조 유형은 범위가 손실 된 후 변수 만 삭제됩니다. 객체는 나중에 가비지 수집기에 의해 파괴됩니다.
- 구조체를 다른 구조체로 복사하면 해당 구조체의 새 복사본이 생성되어 한 구조체가 수정 되어도 다른 구조체의 값에 영향을주지 않습니다.
- 클래스를 다른 클래스로 복사하면 참조 변수 만 복사됩니다.
- 두 참조 변수는 모두 힙의 동일한 개체를 가리 킵니다. 한 변수를 변경하면 다른 참조 변수에 영향을줍니다.
- 구조체는 소멸자 를 가질 수 없지만 클래스는 소멸자를 가질 수 있습니다.
- 구조체는 명시 적 매개 변수없는 생성자를 가질 수 없지만 클래스는 상속을 지원하지 않지만 클래스는 할 수 있습니다. 둘 다 인터페이스에서 상속을 지원합니다.
- 구조체는 밀폐형입니다 .
Microsoft의 클래스와 구조체 선택 에서 ...
경험상 프레임 워크에있는 대부분의 유형은 클래스 여야합니다. 그러나 값 유형의 특성으로 인해 구조체를 사용하는 것이 더 적절한 상황이 있습니다.
✓ 클래스 대신 구조체 를 고려하십시오 .
- 유형의 인스턴스가 작고 일반적으로 수명이 짧거나 일반적으로 다른 개체에 포함 된 경우.
X 형식 에 다음 특성 이 모두 없는 경우 구조체 를 피합니다.
- 기본 유형 (int, double 등)과 유사한 단일 값을 논리적으로 나타냅니다.
- 인스턴스 크기가 16 바이트 미만입니다.
- 불변입니다. (변경 불가)
- 자주 박스에 넣을 필요는 없습니다.
다른 답변에 설명 된 모든 차이점 외에도 :
- 구조체 는 명시적인 매개 변수없는 생성자를 가질 수 없지만 클래스는
- 구조체 는 소멸자를 가질 수 없지만 클래스는
- 구조체 는 다른 구조체 나 클래스에서 상속 할 수 없지만 클래스는 다른 클래스에서 상속 할 수 있습니다. (구조체와 클래스 모두 인터페이스에서 구현할 수 있습니다.)
모든 차이점을 설명하는 비디오를보고 싶다면 Part 29-C # Tutorial-Difference between classes and structs in C #을 확인 하세요.
클래스 인스턴스는 관리되는 힙에 저장됩니다. 인스턴스를 '포함하는'모든 변수는 단순히 힙의 인스턴스에 대한 참조입니다. 메서드에 개체를 전달하면 개체 자체가 아닌 참조 복사본이 전달됩니다.
구조 (기술적으로 값 유형)는 기본 유형처럼 사용되는 위치에 저장됩니다. 컨텐츠는 사용자 정의 된 복사 생성자를 호출하지 않고 언제든지 런타임에 의해 복사 될 수 있습니다. 값 형식을 메서드에 전달하려면 사용자 지정 가능한 코드를 호출하지 않고 전체 값을 다시 복사해야합니다.
구분은 C ++ / CLI 이름에 의해 더 나아집니다. "ref class"는 먼저 설명한 클래스이고, "value class"는 두 번째로 설명한 클래스입니다. C #에서 사용되는 "class"및 "struct"키워드는 단순히 배워야 할 것입니다.
구조 대 클래스
구조는 값 유형이므로 스택에 저장되지만 클래스는 참조 유형이며 힙에 저장됩니다.
구조는 상속과 다형성을 지원하지 않지만 클래스는 둘 다 지원합니다.
기본적으로 모든 struct 멤버는 public이지만 클래스 멤버는 기본적으로 private입니다.
구조체는 값 유형이므로 구조체 객체에 null을 할당 할 수 없지만 클래스의 경우는 아닙니다.
완료하기 위해 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 좌표 저장). 그리고 수업은 다른 사람들에게 적합합니다. 2 명이 같은 이름, 키, 몸무게를 가지고 있어도 여전히 2 명입니다.
다른 답변에 추가하기 위해 주목할 가치가있는 근본적인 차이점이 하나 있습니다. 그것이 메모리에 저장되는 방식입니다. 이는 어레이 성능에 큰 영향을 미칠 수 있습니다. 구조체는 값 유형이므로 가리키는 메모리 영역에 값을 저장하고 클래스는 참조 유형이므로 가리키는 메모리 영역의 클래스를 참조하고 실제 값은 다른 곳에 저장됩니다.
- 구조체를 사용하면 데이터를 저장하기 위해 포함하는 클래스 내에 메모리가 할당됩니다.
- 클래스를 사용하면 포함하는 클래스는 다른 메모리 영역에있는 새 클래스에 대한 포인터 만 포함합니다.
이것은 배열에서도 마찬가지이므로 구조체의 배열은 메모리에서 다음과 같이 보입니다.
[struct][struct][struct][struct][struct][struct][struct][struct]
클래스 배열은 다음과 같습니다.
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
관심있는 실제 값은 실제로 배열에 저장되지 않고 메모리의 다른 곳에 저장됩니다.
대부분의 응용 프로그램에서이 차이는 실제로 중요하지 않지만 고성능 코드에서는 메모리 내의 데이터 위치에 영향을 미치고 CPU 캐시의 성능에 큰 영향을 미칩니다. 구조체를 사용할 수 있거나 사용해야 할 때 클래스를 사용하면 CPU의 캐시 미스 수가 크게 증가합니다.
최신 CPU가 수행하는 가장 느린 일은 크런치 숫자가 아니라 메모리에서 데이터를 가져 오는 것이며, L1 캐시 적중은 RAM에서 데이터를 읽는 것보다 몇 배 더 빠릅니다.
음, 우선 구조체는 참조가 아닌 값으로 전달됩니다. 구조체는 비교적 단순한 데이터 구조에 적합하지만 클래스는 다형성과 상속을 통해 아키텍처 관점에서 훨씬 더 많은 유연성을 제공합니다.
다른 것들은 아마도 나보다 더 자세한 정보를 줄 수 있지만, 내가 원하는 구조가 단순 할 때 구조체를 사용합니다.
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | 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 |
| Access Modifiers | public, private, internal | public, protected, internal, protected internal, private protected |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
액세스 지정자의 기본적인 차이점 외에도 위에서 언급 한 몇 가지 사항 외에도 출력이있는 코드 샘플을 통해 위에서 언급 한 몇 가지를 포함하여 몇 가지 주요 차이점을 추가하여 참조 및 값에 대한보다 명확한 아이디어를 제공하고자합니다.
구조체 :
- 값 유형이며 힙 할당이 필요하지 않습니다.
- 메모리 할당이 다르며 스택에 저장됩니다.
- 작은 데이터 구조에 유용
- 성능에 영향을 미칩니다. 값을 메소드에 전달할 때 전체 데이터 구조를 전달하고 모두 스택에 전달합니다.
- 생성자는 단순히 구조체 값 자체 (일반적으로 스택의 임시 위치)를 반환하고이 값은 필요에 따라 복사됩니다.
- 변수는 각각 고유 한 데이터 사본을 가지며 하나의 작업이 다른 작업에 영향을 미칠 수 없습니다.
- 사용자 지정 상속을 지원하지 않으며 유형 개체에서 암시 적으로 상속합니다.
수업:
- 참조 유형 값
- 힙에 저장
- 동적으로 할당 된 개체에 대한 참조 저장
- 생성자는 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
내부 구조 메서드 내부 구조 개체의 메서드 값 : 20
Struct Object의 메소드 호출 후 값 : 10
Class Object의 초기 값 : 10
내부 클래스 메서드 내부 클래스 개체의 메서드 값 : 20
클래스 개체의 메서드 호출 후 값 : 20
여기에서 값별 호출과 참조 별 호출의 차이점을 명확하게 확인할 수 있습니다.
클래스에서 선언 된 이벤트는 lock (this)을 통해 자동으로 잠금 (정적 이벤트는 클래스 유형에 따라 잠김)을 통해 + = 및-= 액세스를 갖습니다. 구조체에서 선언 된 이벤트는 + = 및-= 액세스가 자동으로 잠기지 않습니다. 구조체에 대한 lock (this)은 참조 유형 표현식에서만 잠글 수 있으므로 작동하지 않습니다.
구조체 인스턴스를 만들면 가비지 컬렉션이 발생할 수 없지만 (생성자가 직접 또는 간접적으로 참조 형식 인스턴스를 만들지 않는 한) 참조 형식 인스턴스를 만들면 가비지 컬렉션이 발생할 수 있습니다.
구조체에는 항상 기본 제공 공용 기본 생성자가 있습니다.
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 { } }
구조체는 소멸자를 가질 수 없습니다. 소멸자는 객체의 재정의 일뿐입니다. 변장으로 마무리하고 값 유형 인 구조체는 가비지 수집의 대상이 아닙니다.
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
구조체는 암시 적으로 봉인되고 클래스는 봉인되지 않습니다.
구조체는 추상이 될 수없고, 클래스는 할 수 있습니다.
구조체는 생성자에서 : base ()를 호출 할 수 없지만 명시적인 기본 클래스가없는 클래스는 호출 할 수 있습니다.
구조체는 다른 클래스를 확장 할 수 없습니다.
구조체는 클래스가 할 수있는 보호 된 멤버 (예 : 필드, 중첩 유형)를 선언 할 수 없습니다.
구조체는 추상 함수 멤버를 선언 할 수 없지만 추상 클래스는 선언 할 수 있습니다.
구조체는 가상 함수 멤버를 선언 할 수없고 클래스는 선언 할 수 있습니다.
구조체는 봉인 된 함수 멤버를 선언 할 수 없지만 클래스는 선언 할 수 있습니다.
구조체는 오버라이드 함수 멤버를 선언 할 수 없으며 클래스는 할 수 있습니다.
이 규칙의 한 가지 예외는 구조체가 System.Object, viz, Equals (), GetHashCode () 및 ToString ()의 가상 메서드를 재정의 할 수 있다는 것입니다.
앞서 언급했듯이 : 클래스는 참조 유형이고 Structs는 모든 결과가 포함 된 값 유형입니다.
일반적으로 Framework Design Guidelines에서는 다음과 같은 경우 클래스 대신 Structs를 사용할 것을 권장합니다.
- 인스턴스 크기가 16 바이트 미만입니다.
- 기본 유형 (int, double 등)과 유사한 단일 값을 논리적으로 나타냅니다.
- 불변입니다
- 자주 박스에 넣지 않아도됩니다.
구조체는 실제 값입니다. 비어있을 수 있지만 null 일 수는 없습니다.
이것은 사실이지만 .NET 2 구조는 Nullable 버전을 지원하고 C #은 사용하기 쉽도록 몇 가지 구문 설탕을 제공합니다.
int? value = null;
value = 1;
"클래스 대 구조체"퍼즐의 흥미로운 경우가 있습니다. 메서드에서 여러 결과를 반환해야하는 상황 : 사용할 항목을 선택합니다. ValueTuple 스토리를 알고 있다면 Tuple (클래스)보다 더 효과적이어야하므로 ValueTuple (구조체)가 추가되었음을 알고 있습니다. 그러나 그것은 숫자로 무엇을 의미합니까? 두 가지 테스트 : 하나는 2 개의 필드가있는 구조체 / 클래스이고, 다른 하나는 8 개의 필드가있는 구조체 / 클래스입니다 (차원이 4보다 큰-클래스는 프로세서 틱 측면에서 구조체보다 더 효과적이어야하지만 물론 GC로드도 고려해야합니다.) ).
PS 특정 사례 '튼튼하거나 컬렉션이있는 클래스'에 대한 또 다른 벤치 마크는 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;
}
}
}
}
기본 값 유형 또는 구조 유형의 모든 변수 또는 필드는 모든 필드 (공용 및 개인)를 포함하여 해당 유형의 고유 한 인스턴스를 보유합니다. 대조적으로, 참조 유형의 변수 또는 필드는 널을 보유하거나 다른 참조가 존재할 수있는 다른 곳에 저장된 객체를 참조 할 수 있습니다. 구조체의 필드는 해당 구조체 유형의 변수 또는 필드와 동일한 위치에 저장되며 스택에 있거나 다른 힙 개체의 일부일 수 있습니다 .
기본 값 유형의 변수 또는 필드를 작성하면 기본값으로 작성됩니다. 구조 유형의 변수 또는 필드를 생성하면 새 인스턴스가 생성되어 기본 방식으로 모든 필드가 생성됩니다. 참조 유형 의 새 인스턴스 생성은 기본 방식으로 모든 필드를 생성 한 다음 유형에 따라 선택적 추가 코드를 실행하는 것으로 시작됩니다.
기본 유형의 한 변수 또는 필드를 다른 변수로 복사하면 값이 복사됩니다. 구조 유형의 하나의 변수 또는 필드를 다른 필드로 복사하면 이전 인스턴스의 모든 필드 (공용 및 개인)가 후자 인스턴스로 복사됩니다. 참조 유형의 한 변수 또는 필드를 다른 변수에 복사하면 후자가 전자 (있는 경우)와 동일한 인스턴스를 참조하게됩니다.
C ++와 같은 일부 언어에서는 형식의 의미 동작이 저장 방법과 무관하지만 .NET에서는 그렇지 않습니다. 유형이 변경 가능한 값 의미 체계를 구현하는 경우 해당 유형의 한 변수를 다른 변수에 복사하면 첫 번째 속성이 두 번째가 참조하는 다른 인스턴스로 복사되고 두 번째 멤버를 사용하여 변경하면 두 번째 인스턴스가 변경됩니다. ,하지만 첫 번째는 아닙니다. 유형이 가변 참조 의미론을 구현하는 경우 한 변수를 다른 변수에 복사하고 두 번째 멤버를 사용하여 객체를 변경하면 첫 번째 변수가 참조하는 객체에 영향을줍니다. 의미가 변경되지 않는 유형은 변형을 허용하지 않으므로 복사가 새 인스턴스를 생성하는지 아니면 첫 번째 인스턴스에 대한 또 다른 참조를 생성하는지 여부는 의미 론적으로 중요하지 않습니다.
.NET에서는 모든 필드가 마찬가지로 수행 할 수있는 경우 값 형식이 위의 의미 체계를 구현할 수 있습니다. 그러나 참조 유형은 변경 가능한 참조 의미론 또는 변경 불가능한 의미 만 구현할 수 있습니다. 가변 참조 유형의 필드가있는 값 유형은 가변 참조 의미론 또는 이상한 하이브리드 의미론 구현으로 제한됩니다.
참고 URL : https://stackoverflow.com/questions/13049/whats-the-difference-between-struct-and-class-in-net
'developer tip' 카테고리의 다른 글
Bash에서 stderr 및 stdout 리디렉션 (0) | 2020.10.02 |
---|---|
"=="와 "is"사이에 차이가 있습니까? (0) | 2020.10.02 |
인쇄 미리보기 모드에서 Chrome의 요소 검사기를 사용하십니까? (0) | 2020.10.02 |
문자열이 숫자인지 식별 (0) | 2020.09.30 |
Git 서브 모듈을 추가 할 때 브랜치 / 태그를 어떻게 지정할 수 있습니까? (0) | 2020.09.30 |