레코드 대 클래스 대 구조를 사용할 때
용?을 사용해야 합니까?
Record
컨트롤러와 서비스 계층 간에 데이터를 이동하는 모든 DTO 클래스의 경우용?을 사용해야 합니까?
Record
으로 저는 제.net API에에 제 합니다.
public class HomeController : Controller
{
public async Task<IActionResult> Search(SearchParameters searchParams)
{
await _service.SearchAsync(searchParams);
}
}
할 것SearchParameters
인기를 얻다Record
?
짧은 버전
데이터 유형이 값 유형이 될 수 있습니까?함께하기struct
아니요? 당신의 유형은 가치와 같은, 가급적이면 불변의 상태를 묘사하나요?와 함께 record
.
사용하다class
그렇지않으면.그래서...
- 예, 용을 사용합니다.
record
단방향 흐름인 경우 DTO에 대한 s. - 요청 은 예, 변할수요바청사인용이게사사다례니입용자상적의 이상적인 사용자 입니다.
record
- 네.
SearchParameters
는 상적인사사다니례입자의 사례입니다.record
.
다음과 같은 실제 사례를 참조하십시오.record
사용, 당신은 이 보고서를 확인할 수 있습니다.
롱 버전
A struct
,aclass
a 리고a.record
사용자 데이터 유형입니다.
구조는 값 유형입니다.클래스는 참조 유형입니다.레코드는 기본적으로 변경할 수 없는 참조 유형입니다.
상속 또는 데이터 유형을 설명하기 위해 계층 구조가 필요한 경우struct
다른것가기키리을을 struct
또는 기본적으로 다른 것들을 가리키는 것들, 당신은 참조 유형이 필요합니다.
레코드는 기본적으로 유형을 값 지향으로 지정하려는 경우 문제를 해결합니다.레코드는 참조 유형이지만 값 지향적 의미론적입니다.
그런 말이 나온 김에, 스스로에게 이런 질문을 해보세요.
데이터 유형이 다음 규칙을 모두 준수합니까?
- 기본 유형(int, double 등)과 유사한 단일 값을 논리적으로 나타냅니다.
- 인스턴스 크기가 16바이트 미만입니다.
- 그것은 불변입니다.
- 그것은 자주 상자에 넣을 필요가 없을 것입니다.
- 그래요? 그것은.
struct
. - 아닌가요? 어떤 참조 유형이어야 합니다.
데이터 유형에 복잡한 값이 포함되어 있습니까?값은 불변입니까?단방향(일방향) 흐름으로 사용하십니까?
- 네? 그렇게 하세요.
record
. - 요? 안돼요로 해요?
class
.
BTW: 익명의 물건들을 잊지 마세요.C# 10.0에 익명의 기록이 있을 것입니다.
메모들
레코드 인스턴스를 변경할 수 있도록 설정하면 해당 레코드 인스턴스를 변경할 수 있습니다.
class Program
{
static void Main()
{
var test = new Foo("a");
Console.WriteLine(test.MutableProperty);
test.MutableProperty = 15;
Console.WriteLine(test.MutableProperty);
//test.Bar = "new string"; // will not compile
}
}
record Foo(string Bar)
{
internal double MutableProperty { get; set; } = 10.0;
}
레코드의 할당은 레코드의 얕은 복사입니다. 사본.with
기록의 표현은 얕거나 깊은 복사가 아닙니다.복사본은 C# 컴파일러에서 내보내는 특수 복제 방법에 의해 만들어집니다.값 형식 구성원이 복사되고 상자에 표시됩니다.참조 유형 구성원이 동일한 참조를 가리킵니다.레코드에 값 유형 속성만 있는 경우에만 레코드의 전체 복사를 수행할 수 있습니다.레코드의 참조 유형 구성원 속성은 얕은 복사본으로 복사됩니다.
다음 예를 참조하십시오(C# 9.0의 최상위 기능 사용).
using System.Collections.Generic;
using static System.Console;
var foo = new SomeRecord(new List<string>());
var fooAsShallowCopy = foo;
var fooAsWithCopy = foo with { }; // A syntactic sugar for new SomeRecord(foo.List);
var fooWithDifferentList = foo with { List = new List<string>() { "a", "b" } };
var differentFooWithSameList = new SomeRecord(foo.List); // This is the same like foo with { };
foo.List.Add("a");
WriteLine($"Count in foo: {foo.List.Count}"); // 1
WriteLine($"Count in fooAsShallowCopy: {fooAsShallowCopy.List.Count}"); // 1
WriteLine($"Count in fooWithDifferentList: {fooWithDifferentList.List.Count}"); // 2
WriteLine($"Count in differentFooWithSameList: {differentFooWithSameList.List.Count}"); // 1
WriteLine($"Count in fooAsWithCopy: {fooAsWithCopy.List.Count}"); // 1
WriteLine("");
WriteLine($"Equals (foo & fooAsShallowCopy): {Equals(foo, fooAsShallowCopy)}"); // True. The lists inside are the same.
WriteLine($"Equals (foo & fooWithDifferentList): {Equals(foo, fooWithDifferentList)}"); // False. The lists are different
WriteLine($"Equals (foo & differentFooWithSameList): {Equals(foo, differentFooWithSameList)}"); // True. The list are the same.
WriteLine($"Equals (foo & fooAsWithCopy): {Equals(foo, fooAsWithCopy)}"); // True. The list are the same, see below.
WriteLine($"ReferenceEquals (foo.List & fooAsShallowCopy.List): {ReferenceEquals(foo.List, fooAsShallowCopy.List)}"); // True. The records property points to the same reference.
WriteLine($"ReferenceEquals (foo.List & fooWithDifferentList.List): {ReferenceEquals(foo.List, fooWithDifferentList.List)}"); // False. The list are different instances.
WriteLine($"ReferenceEquals (foo.List & differentFooWithSameList.List): {ReferenceEquals(foo.List, differentFooWithSameList.List)}"); // True. The records property points to the same reference.
WriteLine($"ReferenceEquals (foo.List & fooAsWithCopy.List): {ReferenceEquals(foo.List, fooAsWithCopy.List)}"); // True. The records property points to the same reference.
WriteLine("");
WriteLine($"ReferenceEquals (foo & fooAsShallowCopy): {ReferenceEquals(foo, fooAsShallowCopy)}"); // True. !!! fooAsCopy is pure shallow copy of foo. !!!
WriteLine($"ReferenceEquals (foo & fooWithDifferentList): {ReferenceEquals(foo, fooWithDifferentList)}"); // False. These records are two different reference variables.
WriteLine($"ReferenceEquals (foo & differentFooWithSameList): {ReferenceEquals(foo, differentFooWithSameList)}"); // False. These records are two different reference variables and reference type property hold by these records does not matter in ReferenceEqual.
WriteLine($"ReferenceEquals (foo & fooAsWithCopy): {ReferenceEquals(foo, fooAsWithCopy)}"); // False. The same story as differentFooWithSameList.
WriteLine("");
var bar = new RecordOnlyWithValueNonMutableProperty(0);
var barAsShallowCopy = bar;
var differentBarDifferentProperty = bar with { NonMutableProperty = 1 };
var barAsWithCopy = bar with { };
WriteLine($"Equals (bar & barAsShallowCopy): {Equals(bar, barAsShallowCopy)}"); // True.
WriteLine($"Equals (bar & differentBarDifferentProperty): {Equals(bar, differentBarDifferentProperty)}"); // False. Remember, the value equality is used.
WriteLine($"Equals (bar & barAsWithCopy): {Equals(bar, barAsWithCopy)}"); // True. Remember, the value equality is used.
WriteLine($"ReferenceEquals (bar & barAsShallowCopy): {ReferenceEquals(bar, barAsShallowCopy)}"); // True. The shallow copy.
WriteLine($"ReferenceEquals (bar & differentBarDifferentProperty): {ReferenceEquals(bar, differentBarDifferentProperty)}"); // False. Operator with creates a new reference variable.
WriteLine($"ReferenceEquals (bar & barAsWithCopy): {ReferenceEquals(bar, barAsWithCopy)}"); // False. Operator with creates a new reference variable.
WriteLine("");
var fooBar = new RecordOnlyWithValueMutableProperty();
var fooBarAsShallowCopy = fooBar; // A shallow copy, the reference to bar is assigned to barAsCopy
var fooBarAsWithCopy = fooBar with { }; // A deep copy by coincidence because fooBar has only one value property which is copied into barAsDeepCopy.
WriteLine($"Equals (fooBar & fooBarAsShallowCopy): {Equals(fooBar, fooBarAsShallowCopy)}"); // True.
WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // True. Remember, the value equality is used.
WriteLine($"ReferenceEquals (fooBar & fooBarAsShallowCopy): {ReferenceEquals(fooBar, fooBarAsShallowCopy)}"); // True. The shallow copy.
WriteLine($"ReferenceEquals (fooBar & fooBarAsWithCopy): {ReferenceEquals(fooBar, fooBarAsWithCopy)}"); // False. Operator with creates a new reference variable.
WriteLine("");
fooBar.MutableProperty = 2;
fooBarAsShallowCopy.MutableProperty = 3;
fooBarAsWithCopy.MutableProperty = 3;
WriteLine($"fooBar.MutableProperty = {fooBar.MutableProperty} | fooBarAsShallowCopy.MutableProperty = {fooBarAsShallowCopy.MutableProperty} | fooBarAsWithCopy.MutableProperty = {fooBarAsWithCopy.MutableProperty}"); // fooBar.MutableProperty = 3 | fooBarAsShallowCopy.MutableProperty = 3 | fooBarAsWithCopy.MutableProperty = 3
WriteLine($"Equals (fooBar & fooBarAsShallowCopy): {Equals(fooBar, fooBarAsShallowCopy)}"); // True.
WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // True. Remember, the value equality is used. 3 != 4
WriteLine($"ReferenceEquals (fooBar & fooBarAsShallowCopy): {ReferenceEquals(fooBar, fooBarAsShallowCopy)}"); // True. The shallow copy.
WriteLine($"ReferenceEquals (fooBar & fooBarAsWithCopy): {ReferenceEquals(fooBar, fooBarAsWithCopy)}"); // False. Operator with creates a new reference variable.
WriteLine("");
fooBarAsWithCopy.MutableProperty = 4;
WriteLine($"fooBar.MutableProperty = {fooBar.MutableProperty} | fooBarAsShallowCopy.MutableProperty = {fooBarAsShallowCopy.MutableProperty} | fooBarAsWithCopy.MutableProperty = {fooBarAsWithCopy.MutableProperty}"); // fooBar.MutableProperty = 3 | fooBarAsShallowCopy.MutableProperty = 3 | fooBarAsWithCopy.MutableProperty = 4
WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // False. Remember, the value equality is used. 3 != 4
WriteLine("");
var venom = new MixedRecord(new List<string>(), 0); // Reference/Value property, mutable non-mutable.
var eddieBrock = venom;
var carnage = venom with { };
venom.List.Add("I'm a predator.");
carnage.List.Add("All I ever wanted in this world is a carnage.");
WriteLine($"Count in venom: {venom.List.Count}"); // 2
WriteLine($"Count in eddieBrock: {eddieBrock.List.Count}"); // 2
WriteLine($"Count in carnage: {carnage.List.Count}"); // 2
WriteLine($"Equals (venom & eddieBrock): {Equals(venom, eddieBrock)}"); // True.
WriteLine($"Equals (venom & carnage): {Equals(venom, carnage)}"); // True. Value properties has the same values, the List property points to the same reference.
WriteLine($"ReferenceEquals (venom & eddieBrock): {ReferenceEquals(venom, eddieBrock)}"); // True. The shallow copy.
WriteLine($"ReferenceEquals (venom & carnage): {ReferenceEquals(venom, carnage)}"); // False. Operator with creates a new reference variable.
WriteLine("");
eddieBrock.MutableList = new List<string>();
eddieBrock.MutableProperty = 3;
WriteLine($"Equals (venom & eddieBrock): {Equals(venom, eddieBrock)}"); // True. Reference or value type does not matter. Still a shallow copy of venom, still true.
WriteLine($"Equals (venom & carnage): {Equals(venom, carnage)}"); // False. the venom.List property does not points to the same reference like in carnage.List anymore.
WriteLine($"ReferenceEquals (venom & eddieBrock): {ReferenceEquals(venom, eddieBrock)}"); // True. The shallow copy.
WriteLine($"ReferenceEquals (venom & carnage): {ReferenceEquals(venom, carnage)}"); // False. Operator with creates a new reference variable.
WriteLine($"ReferenceEquals (venom.List & carnage.List): {ReferenceEquals(venom.List, carnage.List)}"); // True. Non mutable reference type.
WriteLine($"ReferenceEquals (venom.MutableList & carnage.MutableList): {ReferenceEquals(venom.MutableList, carnage.MutableList)}"); // False. This is why Equals(venom, carnage) returns false.
WriteLine("");
public record SomeRecord(List<string> List);
public record RecordOnlyWithValueNonMutableProperty(int NonMutableProperty);
record RecordOnlyWithValueMutableProperty
{
internal int MutableProperty { get; set; } = 1; // this property gets boxed
}
record MixedRecord(List<string> List, int NonMutableProperty)
{
internal List<string> MutableList { get; set; } = new();
internal int MutableProperty { get; set; } = 1; // this property gets boxed
}
여기서 수행 패널티는 명백합니다.레코드 인스턴스에서 복사할 데이터가 클수록 성능 저하가 커집니다.일반적으로 작고 얇은 클래스를 만들어야 하며 이 규칙은 레코드에도 적용됩니다.
당신의 애플리케이션이 데이터베이스나 파일 시스템을 사용하고 있다면, 저는 이 페널티에 대해 크게 걱정하지 않을 것입니다.데이터베이스/파일 시스템 작업 속도는 일반적으로 느립니다.
저는 수업이 이기는 합성 테스트(아래 전체 코드)를 만들었지만 실제 응용에서는 그 영향이 눈에 띄지 않을 것입니다.
또한 성능이 항상 최우선인 것은 아닙니다.요즘에는 고도로 최적화된 스파게티 코드보다 코드의 유지보수성과 가독성이 더 좋습니다.코드 작성자가 어떤 방식을 선호하는지가 코드 작성자의 선택입니다.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace SmazatRecord
{
class Program
{
static void Main()
{
var summary = BenchmarkRunner.Run<Test>();
}
}
public class Test
{
[Benchmark]
public int TestRecord()
{
var foo = new Foo("a");
for (int i = 0; i < 10000; i++)
{
var bar = foo with { Bar = "b" };
bar.MutableProperty = i;
foo.MutableProperty += bar.MutableProperty;
}
return foo.MutableProperty;
}
[Benchmark]
public int TestClass()
{
var foo = new FooClass("a");
for (int i = 0; i < 10000; i++)
{
var bar = new FooClass("b")
{
MutableProperty = i
};
foo.MutableProperty += bar.MutableProperty;
}
return foo.MutableProperty;
}
}
record Foo(string Bar)
{
internal int MutableProperty { get; set; } = 10;
}
class FooClass
{
internal FooClass(string bar)
{
Bar = bar;
}
internal int MutableProperty { get; set; }
internal string Bar { get; }
}
}
결과:
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.1379 (1909/November2018Update/19H2)
AMD FX(tm)-8350, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.103
[Host] : .NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT
DefaultJob : .NET Core 5.0.3 (CoreCLR 5.0.321.7212, CoreFX 5.0.321.7212), X64 RyuJIT
방법 | 의미하다 | 오류 | 표준 개발 |
---|---|---|---|
테스트 기록 | 120.19 μs | 2.195μs | 2.150 μs |
테스트 클래스 | 98.91 μs | 0.856 μs | 0.800μs |
위의 답변은 매우 정확하고 완벽하지만 중요한 유형인 읽기 전용 구조(C#9)와 곧 출시될 레코드 구조(C#10)가 누락되었습니다.
C#과 .을 찾을 때.새로운 도메인에서 사용되는 순으로, 일부 문제가 더 두드러집니다.컴퓨팅 오버헤드에 대해 평균보다 더 중요한 환경의 예로 다음을 들 수 있습니다.
- 컴퓨팅 비용이 청구되고 응답성이 경쟁 우위에 있는 클라우드/데이터 센터 시나리오.
- 게임/VR/AR 및 대기 시간에 대한 소프트 실시간 요구사항
만약 내가 틀렸다면 고쳐주되, 나는 일반적인 규칙을 따를 것입니다.
class
/record
/ValueObject
:
- 유형; 기준유형;;
ref
그리고.in
키워드가 필요하지 않습니다. - 힙이 할당되었습니다. GC에 대한 작업이 더 많습니다.
- 비공개 매개 변수 없는 생성자를 허용합니다.
- 및 전유, 다성및을 허용합니다.
interface
실행. - 상자에 넣을 필요가 없습니다.
- 사용하다
record
DTO 및 불변/값 객체로 사용할 수 있습니다. - 사용하다
ValueObject
이 둘 다 할 때.IComparable
또는 동등성 검사에 대한 정밀한 제어.
(readonly
/record
)struct
:
- 유형. 값유와 함께 할 수 . 읽기 전용 참조로 전달할 수 있습니다.
in
키워드 - 스택 할당됨, 클라우드/데이터 센터/게임/VR/AR에 적합함.
- 공개되지 않은 매개 변수 없는 생성자를 허용하지 않습니다.
- , 유전, 다을지허않지만하용,
interface
실행. - 자주 박스에 넣어야 할 수도 있습니다.
구조 유형을 사용하여 값이 동일하고 동작이 거의 없거나 전혀 없는 데이터 중심 유형을 설계할 수 있습니다.그러나 상대적으로 큰 데이터 모델의 경우 구조 유형에는 다음과 같은 몇 가지 단점이 있습니다.
- 그들은 상속을 지지하지 않습니다.
- 그들은 가치 평등을 결정하는 데 덜 효율적입니다.값 유형의 경우
ValueType.Equals
메소드는 반사를 사용하여 모든 필드를 찾습니다.레코드의 경우 컴파일러가 Equals 메서드를 생성합니다.실제로는 기록에서 가치 평등을 구현하는 것이 훨씬 더 빠릅니다. - 모든 인스턴스에 모든 데이터의 전체 복사본이 있기 때문에 일부 시나리오에서는 더 많은 메모리를 사용합니다.레코드 유형은 참조 유형이므로 레코드 인스턴스에는 데이터에 대한 참조만 포함됩니다.
레코드는 가변적일 수 있지만 주로 불변 데이터 모델을 지원하기 위한 것입니다.레코드 유형은 다음과 같은 기능을 제공합니다.
불변 속성을 가진 참조 유형을 만들기 위한 간결한 구문
동등한 가치
비파괴 돌연변이를 위한 간결한 구문
디스플레이를 위한 기본 제공 형식
상속 계층 지원
레코드 유형에는 몇 가지 단점이 있습니다.
C# 레코드는 IComparable 인터페이스를 구현하지 않습니다.
캡화의관보면서점에슐,
records
씬낫니다습보다 훨씬 더 .structs
변수 수 구조에서매변없생를숨성때길문에,Record
캡슐화 상태가 여전히 불량하여 잘못된 상태의 개체를 인스턴스화할 수 있습니다.평등 검사를 제어할 수 없습니다.
C# 사용 사례 기록:
레코드는 C#의 Fluent Interface 패턴을 대체합니다.여기서는 테스트 데이터 작성기 패턴이 좋은 예입니다.이제 자신만의 상용어구 코드를 작성하는 대신 새로운 기능을 사용하여 많은 시간과 노력을 절약할 수 있습니다.
기록은 DTO에 적합합니다.
데이터베이스에 데이터를 로드하거나 데이터베이스에서 데이터를 검색하거나 사전 처리를 수행하는 동안 중간 데이터 클래스가 필요할 수도 있습니다.이는 위의 DTO와 유사하지만 애플리케이션과 외부 시스템 간의 데이터 계약 역할을 하는 대신 이러한 데이터 클래스가 시스템의 서로 다른 계층 간의 DTO 역할을 합니다.C# 레코드는 그것에 대해서도 훌륭합니다.
마지막으로, 모든 애플리케이션에 풍부하고 완전히 캡슐화된 도메인 모델이 필요한 것은 아닙니다.캡슐화가 많이 필요하지 않은 가장 간단한 경우 C# 레코드를 사용하면 됩니다.그렇지 않으면 DDD 값 개체를 사용합니다.
레코드는 기본적으로 데이터를 저장하는 유형에 대한 간결한 구문을 제공합니다.객체 지향 클래스의 기본적인 용도는 책임을 정의하는 것입니다.
Microsoft에서:
레코드는 유형을 정의하는 또 다른 방법을 추가합니다.당신은 용자를 합니다.
class
개체의 책임 및 동작에 초점을 맞춘 개체 지향 계층 구조를 만드는 정의입니다.사용자가 작성한struct
데이터를 저장하고 효율적으로 복사할 수 있을 정도로 작은 데이터 구조의 유형입니다.사용자가 작성한record
값 기반 동일성 및 비교, 값 복사 및 참조 변수 사용을 원하는 경우를 입력합니다.사용자가 작성한record struct
효율적으로 복사할 수 있을 정도로 작은 유형의 레코드 기능을 원하는 경우를 입력합니다.
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records
언급URL : https://stackoverflow.com/questions/64816714/when-to-use-record-vs-class-vs-struct
'programing' 카테고리의 다른 글
mongodb에서 $와 $match를 사용합니다. (0) | 2023.05.24 |
---|---|
Windows 배치: 절전 (0) | 2023.05.24 |
특정 저장소에 대해서만 GIT_SSL_NO_VERIFY를 설정하려면 어떻게 해야 합니까? (0) | 2023.05.24 |
MongoDB 연결 문자열에 대한 내 dbname은 어디서 찾을 수 있습니까? (0) | 2023.05.24 |
ASP.Net: 리터럴 대 레이블 (0) | 2023.05.24 |