昨天講到了不可變的集合,今天再介紹C#中另一個具有不可變特性的的元素-Record,進入用C#寫FP的重點了!
如何建立一個Record
// 跟class一樣可以設定欄位屬性,可以設定欄位的的存取層級(public/private)
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
};
// 也可以寫成位置屬性,搭配pattern match的position pattern解構很方便
public record Person(string FirstName, string LastName);
// 可同時具有位置屬性與欄位屬性
public record Person(string FirstName, string LastName)
{
public string[] PhoneNumbers { get; init; } = Array.Empty<string>();
// 也可以具有方法
public bool HasPhoneNumbers => PhoneNumbers.Any()
};
如果用位置屬性的方式宣告,就具有不變性,但是欄位屬性仍能夠設定成{get;set;}
,也就是可變得
直接幫你實做了IEquatable<T>
// 原來的類別
class PersonClass
{
public string name { get; set; }
}
var person1 = new PersonClass { name = "安妮雅" };
var person2 = new PersonClass { name = "安妮雅" };
Console.WriteLine(person1 == person2 );
// false,兩個person參考的物件實際上不同
// 使用record
record PersonRecord(string name)
PresonRecord person3 = new("約兒");
PresonRecord person4 = new("約兒");
Console.WriteLine(person3 == person4 );
// true,record有實作
可以繼承
public abstract record Person(string FirstName, string LastName);
public record Doctor(string FirstName, string LastName)
: Person(FirstName, LastName);
public record Spy(string FirstName, string LastName)
: Person(FirstName, LastName);
public static void Main()
{
Person doctor = new Doctor("洛伊德", "佛傑");
Person spy = new Spy("洛伊德", "佛傑");
Console.WriteLine(doctor == spy);
// False
}
如果想要創造一個新的實例,而且內容跟原來只有一點點差異可以使用with
public record Person(string FirstName, string LastName)
Person person1 = new("洛伊德", "佛傑");
Console.WriteLine(person1);
// output: Person { FirstName = "洛伊德", LastName = "佛傑" }
Person person2 = person1 with { FirstName = "約兒" };
Console.WriteLine(person2);
// output: Person { FirstName = "約兒", LastName = "佛傑" }
Record其實是class的語法糖,C#在背後默默的幫你時做了不變性以及可比較,在OO語言裡面比較常看到作為value object使用。在一開始的地方有提到Record可以具有方法成員,但其實不建議,在FP的風格中我們會把資料與行為分開來,而方法會放在模組(module)中,C#中對應到可以用Record(資料)與靜態類別(模組)。明天會進一步介紹Record的使用方法