หากคุณชอบหรือใช้โครงการนี้โปรดให้ดาว ขอบคุณ!
Vogen ( ออกเสียงว่า "Voh Jen" ) เป็นเครื่องกำเนิดไฟฟ้าและเครื่องวิเคราะห์ มันเปลี่ยนดั้งเดิมของคุณ (ints, decimals ฯลฯ ) เป็นวัตถุค่าที่แสดงถึงแนวคิดของโดเมน (customerId, บัญชีความสมดุล ฯลฯ )
มันเพิ่มข้อผิดพลาดการรวบรวม C# ใหม่เพื่อช่วยหยุดการสร้างวัตถุค่าที่ไม่ถูกต้อง
readme นี้ครอบคลุมฟังก์ชั่นบางอย่าง โปรดดู Wiki สำหรับข้อมูลรายละเอียดเพิ่มเติมเช่นการเริ่มต้นการสอนและวิธีการอย่างไร
ตัวสร้างแหล่งกำเนิดสร้าง แนวคิดโดเมน ที่พิมพ์อย่างมาก คุณให้สิ่งนี้:
[ ValueObject < int > ]
public partial struct CustomerId ;... และ Vogen สร้างแหล่งที่มาคล้ายกับสิ่งนี้:
public partial struct CustomerId :
System . IEquatable < CustomerId > ,
System . IComparable < CustomerId > , System . IComparable
{
private readonly int _value ;
public readonly int Value => _value ;
public CustomerId ( ) {
throw new Vogen . ValueObjectValidationException (
"Validation skipped by attempting to use the default constructor..." ) ;
}
private CustomerId ( int value ) => _value = value ;
public static CustomerId From ( int value ) {
CustomerId instance = new CustomerId ( value ) ;
return instance ;
}
public readonly bool Equals ( CustomerId other ) .. .
public readonly bool Equals ( int primitive ) .. .
public readonly override bool Equals ( object obj ) .. .
public static bool operator == ( CustomerId left , CustomerId right ) .. .
public static bool operator != ( CustomerId left , CustomerId right ) .. .
public static bool operator == ( CustomerId left , int right ) .. .
public static bool operator != ( CustomerId left , int right ) .. .
public static bool operator == ( int left , CustomerId right ) .. .
public static bool operator != ( int left , CustomerId right ) .. .
public readonly override int GetHashCode ( ) .. .
public readonly override string ToString ( ) .. .
} จากนั้นคุณใช้ CustomerId แทน int ในโดเมนของคุณในความรู้เต็มรูปแบบว่ามันถูกต้องและปลอดภัยในการใช้งาน:
CustomerId customerId = CustomerId . From ( 123 ) ;
SendInvoice ( customerId ) ;
.. .
public void SendInvoice ( CustomerId customerId ) { .. . } int เป็นประเภทเริ่มต้นสำหรับวัตถุค่า โดยทั่วไปเป็นความคิดที่ดีที่จะประกาศแต่ละประเภทอย่างชัดเจนเพื่อความชัดเจน นอกจากนี้คุณยังสามารถ - เป็นรายบุคคลหรือทั่วโลก - กำหนดค่าให้เป็นประเภทอื่น ๆ ดูส่วนการกำหนดค่าในภายหลังในเอกสาร
นี่คือตัวอย่างอื่น ๆ :
[ ValueObject < decimal > ]
public partial struct AccountBalance ;
[ ValueObject < string > ]
public partial class LegalEntityName ;เป้าหมายหลักของ Vogen คือ การรับรองความถูกต้องของวัตถุค่าของคุณ ตัววิเคราะห์รหัสช่วยให้คุณหลีกเลี่ยงข้อผิดพลาดซึ่งอาจทำให้คุณมีวัตถุค่าที่ไม่ได้รับการกำหนดค่าในโดเมนของคุณ
มันทำได้โดย การเพิ่มข้อ จำกัด ใหม่ในรูปแบบของข้อผิดพลาดการรวบรวม C# ใหม่ มีหลายวิธีที่คุณสามารถลงเอยด้วยวัตถุค่าที่ไม่ได้กำหนดค่า วิธีหนึ่งคือการให้ตัวสร้างประเภทของคุณ การจัดหาตัวสร้างของคุณเองอาจหมายความว่าคุณลืมกำหนดค่าดังนั้น Vogen ไม่อนุญาตให้คุณมีตัวสร้างที่กำหนดไว้ :
[ ValueObject ]
public partial struct CustomerId
{
// Vogen deliberately generates this so that you can't create your own:
// error CS0111: Type 'CustomerId' already defines a member called 'CustomerId'
// with the same parameter type
public CustomerId ( ) { }
// error VOG008: Cannot have user defined constructors,
// please use the From method for creation.
public CustomerId ( int value ) { }
}นอกจากนี้ Vogen จะพบปัญหาเมื่อ สร้าง หรือ บริโภค วัตถุที่มีค่า:
// catches object creation expressions
var c = new CustomerId ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
CustomerId c = default ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
var c = default ( CustomerId ) ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
var c = GetCustomerId ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
var c = Activator . CreateInstance < CustomerId > ( ) ; // error VOG025: Type 'CustomerId' cannot be constructed via Reflection as it is prohibited.
var c = Activator . CreateInstance ( < CustomerId > ) ; // error VOG025: Type 'MyVo' cannot be constructed via Reflection as
it is prohibited
// catches lambda expressions
Func < CustomerId > f = ( ) => default ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
// catches method / local function return expressions
CustomerId GetCustomerId ( ) => default ; // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
CustomerId GetCustomerId ( ) => new CustomerId ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
CustomerId GetCustomerId ( ) => new ( ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
// catches argument / parameter expressions
Task < CustomerId > t = Task . FromResult < CustomerId > ( new ( ) ) ; // error VOG010: Type 'CustomerId' cannot be constructed with 'new' as it is prohibited
void Process ( CustomerId customerId = default ) { } // error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited. หนึ่งในเป้าหมายหลักของโครงการนี้คือการบรรลุ ความเร็วและประสิทธิภาพของหน่วยความจำเกือบเท่ากันเมื่อใช้ดั้งเดิมโดยตรง ใส่อีกวิธีหนึ่งหากทศนิยม decimal ของคุณแสดงถึงยอดคงเหลือในบัญชีจากนั้นมีค่าใช้จ่ายต่ำ มาก ในการใช้วัตถุค่าความ AccountBalance แทน โปรดดูตัวชี้วัดประสิทธิภาพด้านล่าง
Vogen เป็นแพ็คเกจ Nuget ติดตั้งด้วย:
dotnet add package Vogen
เมื่อเพิ่มลงในโครงการของคุณ ตัวสร้างต้นทาง จะสร้าง wrappers สำหรับ primitives ของคุณและ Code Analyzer จะแจ้งให้คุณทราบหากคุณพยายามสร้างวัตถุค่าที่ไม่ถูกต้อง
คิดเกี่ยวกับ แนวคิดโดเมน ของคุณและวิธีที่คุณใช้ Primitives เพื่อเป็นตัวแทนของพวกเขาเช่นแทนที่จะเป็นเช่นนี้:
public void HandlePayment ( int customerId , int accountId , decimal paymentAmount )... มีสิ่งนี้:
public void HandlePayment ( CustomerId customerId , AccountId accountId , PaymentAmount paymentAmount )มันง่ายเหมือนการสร้างประเภทเช่นนี้:
[ ValueObject ]
public partial struct CustomerId ;
[ ValueObject ]
public partial struct AccountId ;
[ ValueObject < decimal > ]
public partial struct PaymentAmount ; ตัวสร้างแหล่งกำเนิดสร้างวัตถุค่า วัตถุที่มีค่าช่วยต่อสู้กับความหลงใหลในการครอบงำโดยการห่อหุ้มดั้งเดิมอย่างง่าย ๆ เช่น int , string , double ฯลฯ ในประเภทที่พิมพ์อย่างรุนแรง
ความหลงใหลดั้งเดิม (aka stringlytyped) หมายถึงการหมกมุ่นอยู่กับดั้งเดิม มันเป็นกลิ่นรหัสที่ลดคุณภาพของซอฟต์แวร์
" ความหลงใหลในยุคแรกคือการใช้ประเภทข้อมูลดั้งเดิมเพื่อแสดงแนวคิดโดเมน " #
ตัวอย่างบางส่วน:
int age - เรา Age age Age อาจมีการตรวจสอบว่ามันไม่สามารถลบได้string postcode - เรามี Postcode postcode Postcode อาจมีการตรวจสอบความถูกต้องในรูปแบบของข้อความเครื่องกำเนิดแหล่งที่มามีความคิดเห็น ความคิดเห็นช่วยให้มั่นใจในความสม่ำเสมอ ความคิดเห็นคือ:
From Age.From(12)Age.From(12) == Age.From(12) )Validate ที่ส่งคืนผล ValidationValueObjectValidationException ใด ๆ ที่ไม่ใช่ Validation.Ok เป็นเรื่องปกติที่จะเป็นตัวแทนของความคิดโดเมนเป็นดั้งเดิม แต่ดั้งเดิมอาจไม่สามารถอธิบายแนวคิดโดเมนได้อย่างเต็มที่
ในการใช้วัตถุค่าแทนดั้งเดิมเราเพียงแค่สลับรหัสเช่นนี้:
public class CustomerInfo {
private int _id ;
public CustomerInfo ( int id ) => _id = id ;
}.. ถึงสิ่งนี้:
public class CustomerInfo {
private CustomerId _id ;
public CustomerInfo ( CustomerId id ) => _id = id ;
} มีโพสต์บล็อกที่นี่ที่อธิบาย แต่จะสรุป:
ความหลงใหลในยุคแรกกำลัง หมกมุ่นอยู่ กับวิธี ที่สะดวกสบาย ที่ดูเหมือนว่า ดั้งเดิมเช่น
intsและstringsช่วยให้เราสามารถเป็นตัวแทนของวัตถุโดเมนและความคิด
มันคือ สิ่งนี้ :
int customerId = 42เกิดอะไรขึ้นกับสิ่งนั้น?
รหัสลูกค้าน่าจะไม่สามารถแสดงได้ อย่างสมบูรณ์ โดย int int อาจเป็นลบหรือเป็นศูนย์ แต่ไม่น่าเป็นไปได้ที่รหัสลูกค้าจะเป็นได้ ดังนั้นเรามี ข้อ จำกัด เกี่ยวกับรหัสลูกค้า เราไม่สามารถ เป็นตัวแทน หรือ บังคับใช้ ข้อ จำกัด เหล่านั้นใน int
ดังนั้นเราต้องมีการตรวจสอบความถูกต้องเพื่อให้แน่ใจว่ามี ข้อ จำกัด ของรหัสลูกค้า เนื่องจากอยู่ใน int เราไม่สามารถตรวจสอบได้ว่ามีการตรวจสอบล่วงหน้าหรือไม่ดังนั้นเราจึงต้องตรวจสอบทุกครั้งที่เราใช้ เนื่องจากมันเป็นแบบดั้งเดิมบางคนอาจเปลี่ยนค่าดังนั้นแม้ว่าเราจะแน่ใจว่าเราได้ตรวจสอบมาก่อน 100% มาก่อนมันอาจต้องตรวจสอบอีกครั้ง
จนถึงตอนนี้เราได้ใช้เป็นตัวอย่างรหัสลูกค้าที่มีค่า 42 ใน C#มันอาจจะไม่แปลกใจเลยที่ " 42 == 42 " ( ฉันไม่ได้ตรวจสอบสิ่งนั้นใน JavaScript! ) แต่ใน โดเมน ของเราควร 42 เสมอ 42 เสมอ? อาจไม่ได้ถ้าคุณเปรียบเทียบรหัสซัพพลายเออร์ 42 กับรหัสลูกค้า 42 ! แต่ดั้งเดิมจะไม่ช่วยคุณที่นี่ (จำไว้ว่า 42 == 42 !)
( 42 == 42 ) // true
( SuppliedId . From ( 42 ) == SupplierId . From ( 42 ) ) // true
( SuppliedId . From ( 42 ) == VendorId . From ( 42 ) ) // compilation error แต่บางครั้งเราต้องแสดงว่าวัตถุค่าไม่ถูกต้องหรือไม่ได้ตั้งค่า เราไม่ต้องการให้ใคร อยู่นอก วัตถุทำสิ่งนี้เพราะมันอาจใช้โดยไม่ตั้งใจ เป็นเรื่องปกติที่จะมีอินสแตนซ์ Unspecified เช่น
public class Person {
public Age Age { get ; } = Age . Unspecified ;
} เราสามารถทำได้ด้วยแอตทริบิวต์ Instance :
[ ValueObject ]
[ Instance ( "Unspecified" , - 1 ) ]
public readonly partial struct Age {
public static Validation Validate ( int value ) =>
value > 0 ? Validation . Ok : Validation . Invalid ( "Must be greater than zero." ) ;
} สิ่งนี้สร้าง public static Age Unspecified = new Age(-1); - ตัวสร้างเป็น private ดังนั้นเฉพาะประเภทนี้เท่านั้นที่สามารถสร้างอินสแตนซ์ ที่ไม่ถูกต้อง
ตอนนี้เมื่อเราใช้ Age การตรวจสอบของเราจะชัดเจนขึ้น:
public void Process ( Person person ) {
if ( person . Age == Age . Unspecified ) {
// age not specified.
}
}นอกจากนี้เรายังสามารถระบุคุณสมบัติอินสแตนซ์อื่น ๆ :
[ ValueObject < float > ]
[ Instance ( "Freezing" , 0 ) ]
[ Instance ( "Boiling" , 100 ) ]
public readonly partial struct Celsius {
public static Validation Validate ( float value ) =>
value >= - 273 ? Validation . Ok : Validation . Invalid ( "Cannot be colder than absolute zero" ) ;
} แต่ละวัตถุค่าสามารถมีการกำหนดค่า ตัวเลือก ของตัวเอง การกำหนดค่ารวมถึง:
หากไม่ได้ระบุไว้ข้างต้นใด ๆ การกำหนดค่าทั่วโลกจะถูกอนุมาน ดูเหมือนว่า:
[ assembly : VogenDefaults (
underlyingType : typeof ( int ) ,
conversions : Conversions . Default ,
throws : typeof ( ValueObjectValidationException ) ) ]สิ่งเหล่านั้นเป็นทางเลือกอีกครั้ง หากพวกเขาไม่ได้ระบุไว้พวกเขาจะผิดนัดเป็น:
typeof(int)Conversions.Default ( TypeConverter และ System.Text.Json )typeof(ValueObjectValidationException)มีคำเตือนการวิเคราะห์รหัสหลายประการสำหรับการกำหนดค่าที่ไม่ถูกต้องรวมถึง:
System.Exception (เพื่อเรียกใช้สิ่งเหล่านี้ด้วยตัวเอง: dotnet run -c Release --framework net9.0 -- --job short --filter * ในโฟลเดอร์ Vogen.Benchmarks )
ดังที่ได้กล่าวไว้ก่อนหน้านี้เป้าหมายของ Vogen คือการบรรลุประสิทธิภาพที่คล้ายกันมากเปรียบเทียบกับการใช้ Primitives ด้วยตนเอง นี่คือเกณฑ์มาตรฐานเปรียบเทียบการใช้วัตถุค่าที่ผ่านการตรวจสอบกับประเภทของ int vs โดยใช้ int โดยธรรมชาติ ( ในปัจจุบัน ?)
BenchmarkDotNet =v0.13.2, OS =Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK =7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
ShortRun : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
Job =ShortRun IterationCount =3 LaunchCount =1
WarmupCount =3 | วิธี | หมายถึง | ข้อผิดพลาด | stddev | อัตราส่วน | อัตราส่วน | Gen0 | จัดสรร |
|---|---|---|---|---|---|---|---|
| โดยใช้อย่างถี่ถ้วน | 14.55 ns | 1.443 ns | 0.079 ns | 1.00 | 0.00 | - | - |
| ใช้ ValueObjectStruct | 14.88 ns | 3.639 ns | 0.199 ns | 1.02 | 0.02 | - | - |
ไม่มีความแตกต่างที่มองเห็นได้ระหว่างการใช้ int ดั้งเดิมและโครงสร้าง VO; ทั้งคู่ค่อนข้างเหมือนกันในแง่ของความเร็วและหน่วยความจำ
สถานการณ์ที่พบบ่อยที่สุดถัดไปคือการใช้คลาส VO เพื่อแสดง String ดั้งเดิม ผลลัพธ์เหล่านี้คือ:
BenchmarkDotNet =v0.13.2, OS =Windows 11 (10.0.22621.1194)
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK =7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
ShortRun : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
Job =ShortRun IterationCount =3 LaunchCount =1
WarmupCount =3 | วิธี | หมายถึง | ข้อผิดพลาด | stddev | อัตราส่วน | อัตราส่วน | Gen0 | จัดสรร | อัตราส่วนการจัดสรร |
|---|---|---|---|---|---|---|---|---|
| การใช้งาน | 151.8 ns | 32.19 | 1.76 | 1.00 | 0.00 | 0.0153 | 256 b | 1.00 |
| ใช้ ValueObjectStruct | 184.8 ns | 12.19 | 0.67 | 1.22 | 0.02 | 0.0153 | 256 b | 1.00 |
มีค่าใช้จ่ายด้านประสิทธิภาพเล็กน้อย แต่การวัดเหล่านี้มีขนาดเล็กอย่างไม่น่าเชื่อ ไม่มีค่าใช้จ่ายหน่วยความจำ
โดยค่าเริ่มต้น VO แต่ละตัวจะได้รับการตกแต่งด้วย TypeConverter และ System.Text.Json (STJ) serializer มีตัวแปลง/serializer อื่น ๆ สำหรับ:
พวกเขาถูกควบคุมโดย enum Conversions ต่อไปนี้มี serializers สำหรับ NSJ และ STJ:
[ ValueObject < float > ( conversions : Conversions . NewtonsoftJson | Conversions . SystemTextJson ) ]
public readonly partial struct Celsius ; หากคุณไม่ต้องการการแปลงใด ๆ ให้ระบุ Conversions.None ไม่มี
หากคุณต้องการการแปลงของคุณเองให้ระบุอีกครั้งและนำไปใช้ด้วยตัวเองเช่นเดียวกับประเภทอื่น ๆ แต่โปรดทราบว่าแม้แต่ serializers จะได้รับข้อผิดพลาดในการรวบรวมเหมือนกันสำหรับ new และ default เมื่อพยายามสร้าง VOS
หากคุณต้องการใช้ dapper อย่าลืมลงทะเบียน - บางอย่างเช่นนี้:
SqlMapper . AddTypeHandler ( new Customer . DapperTypeHandler ( ) ) ;ดูโฟลเดอร์ตัวอย่างสำหรับข้อมูลเพิ่มเติม
สิ่งที่ตามมาคือข้อความที่ตัดตอนมาจากหน้าคำถามที่พบบ่อยเต็มในวิกิ
ใช่อยู่ที่นี่: https://stevedunn.github.io/vogen/vogen.html
เครื่องกำเนิดไฟฟ้าคือ. NET Standard 2.0 รหัสที่สร้างรอง
หากคุณใช้เครื่องกำเนิดไฟฟ้าในโครงการ. NET Framework และใช้โครงการสไตล์เก่า (โครงการก่อนโครงการ 'SDK Style') คุณจะต้องทำบางสิ่งที่แตกต่างกัน:
PackageReference ในไฟล์. csproj: < ItemGroup >
< PackageReference Include = " Vogen " Version = " [LATEST_VERSION_HERE - E.G. 1.0.18] " PrivateAssets = " all " />
</ ItemGroup >latest (หรืออะไรก็ตามที่ 8 ขึ้นไป): <PropertyGroup>
+ <LangVersion>latest</LangVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
วัตถุค่าคำศัพท์หมายถึงวัตถุขนาดเล็กที่ความเท่าเทียมกันขึ้นอยู่กับมูลค่าและไม่ใช่ตัวตน จาก Wikipedia
ในวิทยาศาสตร์คอมพิวเตอร์วัตถุค่าเป็นวัตถุขนาดเล็กที่แสดงถึงเอนทิตีที่เรียบง่ายซึ่งความเท่าเทียมกันไม่ได้ขึ้นอยู่กับตัวตน: IE วัตถุสองค่าเท่ากันเมื่อพวกเขามีค่าเท่ากันไม่จำเป็นต้องเป็นวัตถุเดียวกัน
ใน DDD วัตถุค่าคือ (อีกครั้งจาก Wikipedia)
... วัตถุค่าเป็นวัตถุที่ไม่เปลี่ยนรูป
public record struct CustomerId(int Value); - ที่ไม่ได้ให้การตรวจสอบ ในการตรวจสอบ Value คุณไม่สามารถใช้ไวยากรณ์ชวเลข (ตัวสร้างหลัก) ดังนั้นคุณต้องทำ:
public record struct CustomerId
{
public CustomerId ( int value ) {
if ( value <= 0 ) throw new Exception ( .. . )
}
} คุณอาจให้ตัวสร้างอื่น ๆ ซึ่งอาจไม่ตรวจสอบข้อมูลดังนั้นจึง อนุญาตให้ข้อมูลไม่ถูกต้องเข้าสู่โดเมนของคุณ ตัวสร้างอื่น ๆ เหล่านั้นอาจไม่ได้รับการยกเว้นหรืออาจโยนข้อยกเว้นที่แตกต่างกัน หนึ่งในความคิดเห็นใน Vogen คือข้อมูลที่ไม่ถูกต้องใด ๆ ที่มอบให้กับวัตถุค่าจะส่ง ValueObjectValidationException
นอกจากนี้คุณยังสามารถใช้ default(CustomerId) เพื่อหลบเลี่ยงการตรวจสอบ ใน Vogen มีเครื่องวิเคราะห์ที่จับสิ่งนี้และล้มเหลวในการสร้างเช่น:
// error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
CustomerId c = default ;
// error VOG009: Type 'CustomerId' cannot be constructed with default as it is prohibited.
var c2 = default ( CustomerId ) ; ใช่. โดยค่าเริ่มต้น VO แต่ละตัวจะได้รับการตกแต่งด้วย TypeConverter และ System.Text.Json (STJ) serializer มีตัวแปลง/serializers อื่น ๆ สำหรับ:
ใช่แม้ว่าจะมีการพิจารณาบางอย่าง โปรดดูหน้า efcore บน wiki แต่ tl; dr คือ:
[ValueObject<string>(conversions: Conversions.EfCoreValueConverter)] และคุณต้องบอกให้ Efcore ใช้ตัวแปลงนั้นในวิธีการ OnModelCreating เช่น: builder . Entity < SomeEntity > ( b =>
{
b . Property ( e => e . Name ) . HasConversion ( new Name . EfCoreValueConverter ( ) ) ;
} ) ;คุณทำได้ แต่เพื่อให้แน่ใจว่ามีความสอดคล้องตลอดทั้งโดเมนของคุณคุณจะต้อง ตรวจสอบทุกที่ และกฎหมายของตื้นบอกว่าเป็นไปไม่ได้:
⚖ กฎหมายของตื้น "เมื่อสิ่งที่จำเป็นต้องเปลี่ยนและ n> 1 ตื้นจะพบสิ่งเหล่านี้มากที่สุด n - 1"
อย่างเป็นรูปธรรม: "เมื่อ 5 สิ่งที่จำเป็นต้องเปลี่ยนแปลงตื้นจะพบมากที่สุด 4 ของสิ่งเหล่านี้"
struct ฉันสามารถห้ามการใช้ CustomerId customerId = default(CustomerId); -ใช่ . เครื่องวิเคราะห์สร้างข้อผิดพลาดในการรวบรวม
struct ฉันสามารถห้ามการใช้ CustomerId customerId = new(CustomerId); -ใช่ . เครื่องวิเคราะห์สร้างข้อผิดพลาดในการรวบรวม
เลขที่ . ตัวสร้างพารามิเตอร์ที่น้อยกว่าจะถูกสร้างขึ้นโดยอัตโนมัติและตัวสร้างที่ใช้ค่าพื้นฐานจะถูกสร้างขึ้นโดยอัตโนมัติ
หากคุณเพิ่มตัวสร้างเพิ่มเติมคุณจะได้รับข้อผิดพลาดในการรวบรวมจากตัวสร้างรหัสเช่น
[ ValueObject < int > ) ]
public partial struct CustomerId {
// Vogen already generates this as a private constructor:
// error CS0111: Type 'CustomerId' already defines a member called 'CustomerId' with the same parameter type
public CustomerId ( ) { }
// error VOG008: Cannot have user defined constructors, please use the From method for creation.
public CustomerId ( int value ) { }
}คุณ ทำได้ แต่คุณจะได้รับการเตือนคอมไพเลอร์ CS0282- ไม่มีการสั่งซื้อที่กำหนดระหว่างฟิลด์ในการประกาศหลายครั้งของคลาสบางส่วนหรือโครงสร้าง 'ประเภท'
ฉันต้องการให้ปกติ/ฆ่าเชื้อค่าที่ใช้เช่นตัดการป้อนข้อมูล เป็นไปได้ไหม
ใช่เพิ่มวิธี NormalizeInput เช่น
private static string NormalizeInput ( string input ) => input . Trim ( ) ;ดู Wiki สำหรับข้อมูลเพิ่มเติม
มันจะดีถ้ามันเป็น แต่ยังไม่ได้อยู่ในปัจจุบัน ฉันเขียนบทความเกี่ยวกับเรื่องนี้ แต่โดยสรุปมีข้อเสนอภาษามายาวนานโดยมุ่งเน้นไปที่ประเภทของมูลค่าที่ไม่สามารถกำหนดได้ การมีประเภทมูลค่าที่ไม่สามารถกำหนดได้เป็นขั้นตอนแรกที่ยอดเยี่ยม แต่ก็มีประโยชน์ที่จะมีบางสิ่งในภาษาเพื่อบังคับใช้การตรวจสอบ ดังนั้นฉันจึงเพิ่มข้อเสนอภาษาสำหรับบันทึกค่าคงที่
หนึ่งในคำตอบในข้อเสนอบอกว่าทีมภาษาตัดสินใจว่านโยบายการตรวจสอบความถูกต้องไม่ควรเป็นส่วนหนึ่งของ C#แต่ได้รับการจัดทำโดยเครื่องกำเนิดแหล่งที่มา
StronglyTypedid นี้มุ่งเน้นไปที่ ID มากขึ้น Vogen มุ่งเน้นไปที่ 'แนวคิดโดเมน' มากขึ้นและข้อ จำกัด ที่เกี่ยวข้องกับแนวคิดเหล่านั้น
stringingtyped นี่เป็นความพยายามครั้งแรกของฉันและไม่ได้สร้างแหล่งที่มา มีค่าใช้จ่ายหน่วยความจำเนื่องจากประเภทฐานเป็นคลาส นอกจากนี้ยังไม่มีเครื่องวิเคราะห์ ตอนนี้มันถูกทำเครื่องหมายว่าเลิกใช้แล้วในความโปรดปรานของ Vogen
ค่าของคล้ายกับ stringlyTyped - ไม่สร้างแหล่งที่มาและไม่มีตัววิเคราะห์ นอกจากนี้ยังผ่อนคลายมากขึ้นและช่วยให้ประเภทคอมโพสิต 'พื้นฐาน'
ValueObjectGenerator คล้ายกับ Vogen แต่มุ่งเน้นไปที่การตรวจสอบความถูกต้องและไม่มีตัววิเคราะห์รหัส
สามารถห่อประเภทใดก็ได้ การทำให้เป็นอนุกรมและการแปลงประเภทมีการใช้งานสำหรับ:
สาย
int
ยาว
สั้น
ไบต์
ลอย (เดี่ยว)
ทศนิยม
สองเท่า
วันที่
เป็นวันที่
อย่างทันเวลา
DateTimeOffset
ตัวชี้
บูล
สำหรับประเภทอื่น ๆ จะมีการใช้การแปลงประเภททั่วไปและ serializer หากคุณกำลังจัดหาตัวแปลงของคุณเองสำหรับการแปลงประเภทและการทำให้เป็นอนุกรมให้ระบุว่า None ตัวแปลงและตกแต่งประเภทของคุณด้วยแอตทริบิวต์สำหรับประเภทของคุณเองเช่น
[ ValueObject < SpecialPrimitive > ( conversions : Conversions . None ) ]
[ System . Text . Json . Serialization . JsonConverter ( typeof ( SpecialPrimitiveJsonConverter ) ) ]
[ System . ComponentModel . TypeConverter ( typeof ( SpecialPrimitiveTypeConverter ) ) ]
public partial struct SpecialMeasurement ; ใช่โดยการระบุประเภทข้อยกเว้นในแอตทริบิวต์ ValueObject หรือทั่วโลกด้วย VogenConfiguration
TimeOnly โดยบอกว่า DateTime ไม่สามารถแปลงเป็น TimeOnly - ฉันควรทำอย่างไร? LINQ2DB 4.0 หรือมากกว่ารองรับ DateOnly และ TimeOnly อย่างมาก Vogen สร้างตัวแปลงค่าสำหรับ LINQ2DB; สำหรับ DateOnly มันใช้งานได้ แต่สำหรับ `Timeonly คุณต้องเพิ่มสิ่งนี้ลงในแอปพลิเคชันของคุณ:
MappingSchema.Default.SetConverter<DateTime, TimeOnly>(dt => TimeOnly.FromDateTime(dt));
ใช่. เพิ่มการพึ่งพาให้กับ Protobuf-Net และตั้งค่าแอตทริบิวต์ตัวแทน:
[ ValueObject < string > ]
[ ProtoContract ( Surrogate = typeof ( string ) ) ]
public partial class BoxId {
//...
}ประเภท BoxID ตอนนี้จะได้รับการจัดลำดับเป็นสตริงในการโทรทั้งหมด/GRPC หากมีการสร้างไฟล์. proto สำหรับแอปพลิเคชันอื่น ๆ จากรหัส C# ไฟล์โปรโตจะรวมประเภทตัวแทนเป็นประเภท ขอบคุณ @domasm สำหรับข้อมูลนี้
ขอบคุณทุกคนที่มีส่วนร่วม!
ฉันได้รับแรงบันดาลใจมากมายจาก Andrew Lock's Stronglypedid
ฉันยังได้รับแนวคิดที่ยอดเยี่ยมจาก meziantou.analyzer ของGéraldBarré