fri*_*ley 65 .net-5 c#-9.0 c#-record-type
我是否应该Record
用于在控制器和服务层之间移动数据的所有 DTO 类?
我是否应该Record
用于所有请求绑定,因为理想情况下我希望发送到控制器的请求对于我的 asp.net api 是不可变的
什么是记录?https://anthonygiretti.com/2020/06/17/introducing-c-9-records/
public class HomeController
{
public IHttpAction Search([FromBody] SearchParameters searchParams)
{
_service.Search(searchParams);
}
}
Run Code Online (Sandbox Code Playgroud)
应该发SearchParameters
一个Record
?
KUT*_*ime 107
你的数据类型可以是值类型吗?与struct
. 不?您的类型是否描述了类似值的,最好是不可变的状态?与record
.
class
否则使用。所以...
record
如果是单向流程,请将 s 用于您的 DTO。record
SearchParameters
是record
.有关更多实际record
使用示例,您可以查看此repo。
A struct
、aclass
和 arecord
是用户数据类型。
结构是值类型。类是引用类型。记录默认是不可变的引用类型。
当您需要某种层次结构来描述您的数据类型(如继承或struct
指向另一个struct
或基本上指向其他事物的事物)时,您需要一个引用类型。
当您希望您的类型默认为面向值时,记录解决了这个问题。记录是引用类型,但具有面向值的语义。
话虽如此,问自己这些问题......
请问您的数据类型尊重所有的这些规则:
struct
.您的数据类型是否封装了某种复杂的值?值是不可变的吗?您是否在单向(单向)流中使用它?
record
.class
.顺便说一句:不要忘记匿名对象。在 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
}
}
public record Foo(string Bar)
{
public double MutableProperty { get; set; } = 10.0;
}
Run Code Online (Sandbox Code Playgroud)
默认情况下,记录的副本是记录的浅拷贝。该副本由 C# 编译器发出的特殊克隆方法创建。值类型成员被装箱。你可以做一个记录的深拷贝。
请参阅此示例(使用 C# 9.0 中的顶级功能):
using System;
using System.Collections.Generic;
using static System.Console;
var foo = new SomeRecord(new List<string>());
var fooAsCopy = foo;
var fooWithDifferentList = foo with { List = new List<string>() { "a", "b" } };
var differentFooWithSameList = new SomeRecord(foo.List);
foo.List.Add("a");
WriteLine($"Count in foo: {foo.List.Count}"); // 1
WriteLine($"Count in fooAsCopy: {fooAsCopy.List.Count}"); // 1
WriteLine($"Count in fooWithDifferentList: {fooWithDifferentList.List.Count}"); // 2
WriteLine($"Count in differentFooWithSameList: {differentFooWithSameList.List.Count}"); // 1
WriteLine($"Equals (foo & fooAsCopy): {Equals(foo, fooAsCopy)}"); // True
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($"ReferenceEquals (foo & fooAsCopy): {ReferenceEquals(foo, fooAsCopy)}"); // True, because pure shallow copy
WriteLine($"ReferenceEquals (foo & fooWithDifferentList): {ReferenceEquals(foo, fooWithDifferentList)}"); // False, the list are different
WriteLine($"ReferenceEquals (foo & differentFooWithSameList): {ReferenceEquals(foo, differentFooWithSameList)}"); // False, the different instances of the same record type
var bar = new SomeRecordWithValueProperty();
var barAsCopy = bar;
var barAsDeepCopy = bar with { }; // A deep copy
WriteLine($"Equals (bar & barAsCopy): {Equals(bar, barAsCopy)}"); // True
WriteLine($"Equals (bar & barAsDeepCopy): {Equals(bar, barAsDeepCopy)}"); // True, value equality
WriteLine($"ReferenceEquals (bar & barAsCopy): {ReferenceEquals(bar, barAsCopy)}"); // True, the shallow copy
WriteLine($"ReferenceEquals (bar & barAsDeepCopy): {ReferenceEquals(bar, barAsDeepCopy)}"); // False, the deep copy
bar.MutableProperty = 2;
barAsCopy.MutableProperty = 3;
barAsDeepCopy.MutableProperty = 3;
WriteLine($"bar.MutableProperty = {bar.MutableProperty} | barAsCopy.MutableProperty = {barAsCopy.MutableProperty} ");
WriteLine($"Equals (bar & barAsCopy): {Equals(bar, barAsCopy)}"); // True, mutable property is boxed and both instance has value 3 in MutableProperty.
WriteLine($"Equals (bar & barAsDeepCopy): {Equals(bar, barAsDeepCopy)}"); // True
WriteLine($"ReferenceEquals (bar & barAsCopy): {ReferenceEquals(bar, barAsCopy)}"); // True
WriteLine($"ReferenceEquals (bar & barAsDeepCopy): {ReferenceEquals(bar, barAsDeepCopy)}"); // False
public record SomeRecord(List<string> List);
public record SomeRecordWithValueProperty
{
public int MutableProperty { get; set; } = 1; // this property gets boxed
}
Run Code Online (Sandbox Code Playgroud)
性能损失在这里很明显。要在您拥有的记录实例中复制更大的数据,您将获得更大的性能损失。通常,您应该创建小而纤细的类,此规则也适用于记录。
如果您的应用程序使用数据库或文件系统,我不会太担心这种惩罚。数据库/文件系统操作通常较慢。
我做了一些综合测试(下面的完整代码),其中类很受欢迎,但在实际应用中,影响应该是不明显的。
此外,性能并不总是第一要务。如今,代码的可维护性和可读性比高度优化的意大利面条式代码更可取。他更喜欢哪种方式是代码作者的选择。
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;
}
}
public record Foo(string Bar)
{
public int MutableProperty { get; set; } = 10;
}
public class FooClass
{
public FooClass(string bar)
{
Bar = bar;
}
public int MutableProperty { get; set; }
public string Bar { get; }
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
方法 | 意思 | 错误 | 标准差 |
---|---|---|---|
测试记录 | 120.19 ?s | 2.299 ?s | 2.150 ?s |
测试类 | 98.91 ?s | 0.856 ?s | 0.800 ?s |
fib*_*iel 46
我真的很喜欢上面的答案,它们非常精确和完整,但我缺少一个重要的类型:readonly struct (C#9)以及即将推出的record struct (C#10)。
随着我们发现 C# 和 .Net 在新领域的使用,一些问题变得更加突出。作为对计算开销比平均水平更重要的环境示例,我可以列出
- 云/数据中心场景,其中计算是计费的并且响应能力是竞争优势。
- 对延迟有软实时要求的游戏/VR/AR
所以,如果我错了,请纠正我,但我会遵循通常的规则:
class
// :record
ValueObject
ref
并且in
不需要关键字。interface
实现。record
DTO 和不可变/值对象。ValueObject
当您既需要不变性又需要对相等性检查进行精确控制时使用IComparable
。( readonly
/ record
) struct
:
in
。interface
允许实现。Ali*_*yat 21
您可以使用结构类型来设计以数据为中心的类型,这些类型提供值相等性和很少或没有行为。但对于比较大的数据模型,结构类型有一些缺点:
\nValueType.Equals
方法使用反射来查找所有字段。为了记录,编译器生成 Equals 方法。在实践中,记录中价值平等的实施速度明显更快。虽然记录可以是可变的,但它们主要用于支持不可变的数据模型。记录类型提供以下功能:
\n用于创建具有不可变属性的引用类型的简明语法\n
\n价值平等
\n非破坏性突变的简洁语法
\n内置显示格式
\n支持继承层次结构
\n记录类型有一些缺点:
\nC# 记录不实现 IComparable 接口
\n就封装性而言,比xe2x80x99records
好得多,因为你不能将无参构造函数隐藏在结构体中,但封装性仍然很差,我们可以实例化一个处于无效状态的对象。structs
Record
无法控制相等性检查
\n记录将取代C# 中的Fluent Interface 模式。测试数据生成器模式就是一个很好的例子。您现在可以使用新的 with 功能,而不是编写自己的样板代码,从而节省大量时间和精力。
\n记录对 DTO 有利
\n在将数据加载到数据库或\n从数据库检索数据或进行某些预处理时,您可能还需要临时数据类。\n这与上述 DTO 类似,但这些数据不是充当应用程序和外部系统之间的数据\n契约,而是\n类充当您自己系统的不同层之间的 DTO。C#\nrecords 也非常适合这样做。
\n最后,并非所有应用程序都需要丰富的、完全封装的域模型。在大多数不需要太多封装的简单情况下,C# 记录就可以了。否则使用DDD 值对象
\n记录为基本用途是存储数据的类型提供简洁的语法。对于面向对象的类,基本用途是定义职责。
来自微软:
记录添加了另一种定义类型的方式。您可以使用
class
定义来创建面向对象的层次结构,重点关注对象的职责和行为。您可以struct
为存储数据且足够小以便高效复制的数据结构创建类型。record
当您需要基于值的相等和比较、不想复制值并且想要使用引用变量时,可以创建 类型。record struct
当您需要足够小的类型记录功能以进行有效复制时,您可以创建 类型。
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records
归档时间: |
|
查看次数: |
15995 次 |
最近记录: |