답변:
.NET에는 두 가지 범주 유형 ( 참조 유형 과 값 유형)이 있습니다. .
구조는 값 유형 이고 클래스는 참조 유형입니다 입니다.
일반적인 차이점은 참조 유형 이 힙에 있고 값 유형이 있다는 것입니다. 인라인으로 유지된다는 것입니다. 즉 변수 또는 필드가 정의 된 위치입니다.
값 유형을 포함 하는 변수 는 전체 값 유형을 포함합니다 값이 포함됩니다. 구조체의 경우 변수에 모든 필드와 함께 구조체 전체가 포함됩니다.
참조 유형을 포함하는 변수 는 포인터 또는 참조를 포함합니다 실제 값이있는 메모리의 다른 곳에 됩니다.
여기에는 다음과 같은 장점이 있습니다.
내부적으로 참조 유형 은 포인터로 구현되며 변수 할당의 작동 방식을 알고 다른 행동 패턴이 있습니다.
변수 또는 필드를 선언 할 때 두 유형의 차이점은 다음과 같습니다.
각각에 대한 간단한 요약 :
수업 만 :
구조 만 :
클래스와 구조 :
c# struct memory overhead
찾았습니다 . 그렇지 않다. 무슨 소리 야 ?
class
가 관리되는 메모리 (가비지 수집기에서 처리됨) 라는 사실을 언급하고 있다고 생각 하지만 인스턴스 struct
는 그렇지 않습니다. .
.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이 될 수 없습니다. 이러한 이유로 구조체에는 항상 매개 변수가없는 기본 생성자가 있습니다. '시작 값'이 필요합니다.
구조체와 클래스의 차이점 :
Microsoft의 클래스와 구조 선택 중 ...
경험상 프레임 워크에서 대부분의 유형은 클래스 여야합니다. 그러나 값 유형의 특성으로 인해 구조체를 사용하는 것이 더 적합한 상황이 있습니다.
✓ 클래스 대신 구조체 를 고려하십시오 .
- 유형의 인스턴스가 작고 일반적으로 수명이 짧거나 다른 개체에 일반적으로 포함 된 경우
X 유형 에 다음 특성 이 모두 없는 경우 구조체 를 피하십시오.
- 기본 유형 (int, double 등)과 유사한 단일 값을 논리적으로 나타냅니다.
- 인스턴스 크기는 16 바이트 미만입니다.
- 불변입니다. (변경 불가)
- 자주 박스에 넣을 필요는 없습니다.
다른 답변에 설명 된 모든 차이점 외에도 :
모든 차이점을 설명하는 비디오를 시청 한 경우 C # 에서 Part 29-C # Tutorial-클래스와 구조체 의 차이점을 확인할 수 있습니다 .
클래스 인스턴스는 관리되는 힙에 저장됩니다. 인스턴스를 포함하는 모든 변수는 단순히 힙의 인스턴스에 대한 참조입니다. 메소드에 오브젝트를 전달하면 오브젝트 자체가 아니라 참조 사본이 전달됩니다.
구조 (기술적으로 가치 유형)는 기본 유형과 매우 유사하게 사용되는 모든 위치에 저장됩니다. 내용은 사용자 정의 된 복사 생성자를 호출하지 않고 언제든지 런타임에 의해 복사 될 수 있습니다. 메소드에 값 유형을 전달하려면 사용자 정의 가능한 코드를 호출하지 않고 전체 값을 다시 복사해야합니다.
C ++ / CLI 이름으로 구별이 더 잘됩니다. "ref class"는 먼저 설명 된 클래스이고 "value class"는 두 번째로 설명 된 클래스입니다. C #에서 사용하는 "class"및 "struct"키워드는 반드시 알아야합니다.
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | 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 |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
구조 대 클래스
구조체는 값 형식이므로 스택에 저장되지만 클래스는 참조 형식이며 힙에 저장됩니다.
구조는 상속과 다형성을 지원하지 않지만 클래스는 둘 다를 지원합니다.
기본적으로 모든 구조체 멤버는 공개이지만 클래스 멤버는 기본적으로 비공개입니다.
구조체는 값 형식이므로 구조체 개체에 null을 할당 할 수 없지만 클래스에는 해당되지 않습니다.
다른 답변에 덧붙이 자면 주목할 가치가있는 근본적인 차이점이 있습니다. 즉, 성능에 큰 영향을 줄 수 있기 때문에 데이터가 배열 내에 저장되는 방식입니다.
따라서 구조체 배열은 메모리에서 이와 같이 보입니다.
[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}");
}
그것을 완성하기 위해, 사용시 또 다른 차이점이 있습니다. 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 좌표 저장)과 같은 숫자 형 객체에 적합합니다. 그리고 수업은 다른 사람들에게 적합합니다. 두 사람의 이름, 키, 몸무게가 동일하더라도 여전히 두 사람입니다.
액세스 지정자의 기본 차이점과 위에서 언급 한 몇 가지 외에도 출력이있는 코드 샘플과 함께 위에서 언급 한 것을 포함하여 주요 차이점을 추가하고 싶습니다. 이는 참조 및 값에 대한 명확한 아이디어를 제공합니다.
구조 :
수업:
코드 샘플
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
여기에서 값별 호출과 참조 별 호출의 차이점을 명확하게 확인할 수 있습니다.
클래스에 선언 된 이벤트는 잠금을 통해 + = 및-= 액세스가 자동으로 잠겨있어 스레드를 안전하게 만듭니다 (정적 이벤트는 클래스 유형에 잠겨 있습니다). 구조체에 선언 된 이벤트에는 + = 및-= 액세스가 자동으로 잠기지 않습니다. 참조 유형 표현식에서만 잠글 수 있으므로 구조체의 잠금 (this)이 작동하지 않습니다.
struct 인스턴스를 작성하면 가비지 콜렉션이 발생할 수 없으며 (생성자가 직접 또는 간접적으로 참조 유형 인스턴스를 작성하지 않는 한) 참조 유형 인스턴스를 작성하면 가비지 콜렉션이 발생할 수 있습니다.
구조체에는 항상 기본 제공 공용 기본 생성자가 있습니다.
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 ()의 가상 메서드를 재정의 할 수 있다는 것입니다.
구조체는 재정의 함수 멤버를 선언 할 수 없으며 클래스는 선언 할 수 있습니다.
Object
를 희망합니다.이 박스는 구조체의 박스 사본에 대한 참조를 보유합니다.
"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;
}
}
}
}
Structs는 실제 값입니다. 비어있을 수 있지만 결코 null이 아닙니다.
그러나 .NET 2 structs는 Nullable 버전을 지원하고 C #은 사용하기 쉽도록 일부 구문 설탕을 제공합니다.
int? value = null;
value = 1;
(object)(default(int?)) == null
설탕 이외의 다른 값 유형으로는 할 수없는 것을 의미 합니다. 유일한 설탕은 int?
입니다 Nullable<int>
.
기본 값 유형 또는 구조 유형의 모든 변수 또는 필드에는 모든 필드 (공용 및 개인)를 포함하여 해당 유형의 고유 한 인스턴스가 있습니다. 대조적으로, 참조 유형의 변수 또는 필드는 널 (null)을 보유 할 수 있거나, 다른 수의 다른 참조가 존재할 수도있는 다른 곳에 저장된 오브젝트를 나타낼 수 있습니다. 구조체의 필드는 스택에 있거나 다른 힙 객체의 일부일 수있는 해당 구조 유형의 변수 또는 필드와 같은 위치에 저장됩니다 .
기본 값 유형의 변수 또는 필드를 작성하면 기본값으로 작성됩니다. 구조 유형의 변수 또는 필드를 작성하면 새 인스턴스가 작성되어 기본 방식으로 모든 필드가 작성됩니다. 참조 유형 의 새 인스턴스 작성 은 기본 방식으로 모든 필드를 작성한 다음 유형에 따라 선택적 추가 코드를 실행하여 시작됩니다.
프리미티브 유형의 한 변수 또는 필드를 다른 유형으로 복사하면 값이 복사됩니다. 하나의 변수 또는 구조 유형의 필드를 다른 유형으로 복사하면 이전 인스턴스의 모든 필드 (공개 및 개인)가 후자의 인스턴스로 복사됩니다. 하나의 변수 또는 참조 유형의 필드를 다른 유형으로 복사하면 후자가 이전과 동일한 인스턴스를 참조합니다 (있는 경우).
C ++과 같은 일부 언어에서 형식의 의미 적 동작은 형식의 저장 방식과 무관하지만 .NET에는 해당되지 않습니다. 유형이 변경 가능한 값 의미를 구현하는 경우 해당 유형의 한 변수를 다른 유형으로 복사하면 첫 번째의 특성이 두 번째 인스턴스에 의해 참조되는 다른 인스턴스로 복사되고 두 번째의 멤버를 사용하여 변경하면 두 번째 인스턴스가 변경됩니다. 하지만 처음은 아닙니다. 유형이 변경 가능한 참조 의미를 구현하는 경우 한 변수를 다른 변수에 복사하고 두 번째 멤버를 사용하여 오브젝트를 변경하면 첫 번째 변수가 참조하는 오브젝트에 영향을 미칩니다. 불변의 의미를 가진 타입은 돌연변이를 허용하지 않기 때문에, 복사가 새로운 인스턴스를 생성하는지 또는 첫 번째에 대한 다른 참조를 생성하는지는 의미 적으로 중요하지 않습니다.
.NET에서는 모든 필드가 동일하게 수행 될 수있는 경우 값 유형이 위의 의미를 구현할 수 있습니다. 그러나 참조 유형은 변경 가능한 참조 시맨틱 또는 변경 불가능한 시맨틱 만 구현할 수 있습니다. 변경 가능한 참조 유형의 필드가있는 값 유형은 변경 가능한 참조 시맨틱 또는 이상한 하이브리드 시맨틱을 구현하는 것으로 제한됩니다.