mirror of
https://github.com/markjprice/cs11dotnet7.git
synced 2025-12-06 05:32:03 +01:00
656 lines
13 KiB
Plaintext
656 lines
13 KiB
Plaintext
|
|
#!markdown
|
||
|
|
|
||
|
|
# Chapter 5 - Building Your Own Types with Object-Oriented Programming
|
||
|
|
|
||
|
|
Execute the following code cell to make `Console` methods available in every code cell in this notebook.
|
||
|
|
|
||
|
|
We cannot create a class library in a .NET Interactive notebook so we will put all code in the same notebook.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using static System.Console;
|
||
|
|
|
||
|
|
using System.Collections.Generic;
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Storing a value using an enum type (defining the enum)
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
[System.Flags]
|
||
|
|
public enum WondersOfTheAncientWorld : byte
|
||
|
|
{
|
||
|
|
None = 0b_0000_0000, // i.e. 0
|
||
|
|
GreatPyramidOfGiza = 0b_0000_0001, // i.e. 1
|
||
|
|
HangingGardensOfBabylon = 0b_0000_0010, // i.e. 2
|
||
|
|
StatueOfZeusAtOlympia = 0b_0000_0100, // i.e. 4
|
||
|
|
TempleOfArtemisAtEphesus = 0b_0000_1000, // i.e. 8
|
||
|
|
MausoleumAtHalicarnassus = 0b_0001_0000, // i.e. 16
|
||
|
|
ColossusOfRhodes = 0b_0010_0000, // i.e. 32
|
||
|
|
LighthouseOfAlexandria = 0b_0100_0000 // i.e. 64
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Defining a class
|
||
|
|
|
||
|
|
.NET Interactive notebooks do not support namespaces. You would get a "Cannot declare namesapce in script code" error. But we can declare a class.
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Storing a value using an enum type (using the enum)
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
public class Person : object
|
||
|
|
{
|
||
|
|
// fields
|
||
|
|
public string Name;
|
||
|
|
public DateTime DateOfBirth;
|
||
|
|
public WondersOfTheAncientWorld FavoriteAncientWonder;
|
||
|
|
public WondersOfTheAncientWorld BucketList;
|
||
|
|
public List<Person> Children = new();
|
||
|
|
|
||
|
|
// constants
|
||
|
|
public const string Species = "Homo Sapien";
|
||
|
|
|
||
|
|
// read-only fields
|
||
|
|
public readonly string HomePlanet = "Earth";
|
||
|
|
public readonly DateTime Instantiated;
|
||
|
|
|
||
|
|
// constructors
|
||
|
|
public Person()
|
||
|
|
{
|
||
|
|
// set default values for fields
|
||
|
|
// including read-only fields
|
||
|
|
Name = "Unknown";
|
||
|
|
Instantiated = DateTime.Now;
|
||
|
|
}
|
||
|
|
|
||
|
|
public Person(string initialName, string homePlanet)
|
||
|
|
{
|
||
|
|
Name = initialName;
|
||
|
|
HomePlanet = homePlanet;
|
||
|
|
Instantiated = DateTime.Now;
|
||
|
|
}
|
||
|
|
|
||
|
|
// methods
|
||
|
|
public void WriteToConsole()
|
||
|
|
{
|
||
|
|
WriteLine($"{Name} was born on a {DateOfBirth:dddd}.");
|
||
|
|
}
|
||
|
|
|
||
|
|
public string GetOrigin()
|
||
|
|
{
|
||
|
|
return $"{Name} was born on {HomePlanet}.";
|
||
|
|
}
|
||
|
|
|
||
|
|
public (string, int) GetFruit()
|
||
|
|
{
|
||
|
|
return ("Apples", 5);
|
||
|
|
}
|
||
|
|
|
||
|
|
public (string Name, int Number) GetNamedFruit()
|
||
|
|
{
|
||
|
|
return (Name: "Apples", Number: 5);
|
||
|
|
}
|
||
|
|
|
||
|
|
public string SayHello()
|
||
|
|
{
|
||
|
|
return $"{Name} says 'Hello!'";
|
||
|
|
}
|
||
|
|
|
||
|
|
public string SayHello(string name)
|
||
|
|
{
|
||
|
|
return $"{Name} says 'Hello {name}!'";
|
||
|
|
}
|
||
|
|
|
||
|
|
public string OptionalParameters(
|
||
|
|
string command = "Run!",
|
||
|
|
double number = 0.0,
|
||
|
|
bool active = true)
|
||
|
|
{
|
||
|
|
return string.Format(
|
||
|
|
format: "command is {0}, number is {1}, active is {2}",
|
||
|
|
arg0: command,
|
||
|
|
arg1: number,
|
||
|
|
arg2: active);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void PassingParameters(int x, ref int y, out int z)
|
||
|
|
{
|
||
|
|
// out parameters cannot have a default
|
||
|
|
// AND must be initialized inside the method
|
||
|
|
z = 99;
|
||
|
|
// increment each parameter
|
||
|
|
x++;
|
||
|
|
y++;
|
||
|
|
z++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// a property defined using C# 1 - 5 syntax
|
||
|
|
public string Origin
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return $"{Name} was born on {HomePlanet}";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// two properties defined using C# 6+ lambda expression body syntax
|
||
|
|
public string Greeting => $"{Name} says 'Hello!'";
|
||
|
|
|
||
|
|
public int Age => System.DateTime.Today.Year - DateOfBirth.Year;
|
||
|
|
|
||
|
|
public string FavoriteIceCream { get; set; } // auto-syntax
|
||
|
|
|
||
|
|
private string favoritePrimaryColor;
|
||
|
|
public string FavoritePrimaryColor
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return favoritePrimaryColor;
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
switch (value.ToLower())
|
||
|
|
{
|
||
|
|
case "red":
|
||
|
|
case "green":
|
||
|
|
case "blue":
|
||
|
|
favoritePrimaryColor = value;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
throw new System.ArgumentException(
|
||
|
|
$"{value} is not a primary color. " +
|
||
|
|
"Choose from: red, green, blue.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// indexers
|
||
|
|
public Person this[int index]
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return Children[index]; // pass on to the List<T> indexer
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
Children[index] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// end of class
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Instantiating a class
|
||
|
|
|
||
|
|
The namespace for a class defined in a .NET Interactive notebook follows the pattern `Submission#[number]+[classname]`.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
// Person bob = new Person(); // C# 1.0 or later
|
||
|
|
// var bob = new Person(); // C# 3.0 or later
|
||
|
|
Person bob = new(); // C# 9.0 or later
|
||
|
|
WriteLine(bob.ToString());
|
||
|
|
|
||
|
|
bob.Name = "Bob Smith";
|
||
|
|
bob.DateOfBirth = new DateTime(1965, 12, 22); // C# 1.0 or later
|
||
|
|
|
||
|
|
WriteLine(format: "{0} was born on {1:dddd, d MMMM yyyy}",
|
||
|
|
arg0: bob.Name,
|
||
|
|
arg1: bob.DateOfBirth);
|
||
|
|
|
||
|
|
Person alice = new()
|
||
|
|
{
|
||
|
|
Name = "Alice Jones",
|
||
|
|
DateOfBirth = new(1998, 3, 7) // C# 9.0 or later
|
||
|
|
};
|
||
|
|
|
||
|
|
WriteLine(format: "{0} was born on {1:dd MMM yy}",
|
||
|
|
arg0: alice.Name,
|
||
|
|
arg1: alice.DateOfBirth);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
bob.FavoriteAncientWonder = WondersOfTheAncientWorld.StatueOfZeusAtOlympia;
|
||
|
|
|
||
|
|
WriteLine(
|
||
|
|
format: "{0}'s favorite wonder is {1}. Its integer is {2}.",
|
||
|
|
arg0: bob.Name,
|
||
|
|
arg1: bob.FavoriteAncientWonder,
|
||
|
|
arg2: (int)bob.FavoriteAncientWonder);
|
||
|
|
|
||
|
|
bob.BucketList =
|
||
|
|
WondersOfTheAncientWorld.HangingGardensOfBabylon
|
||
|
|
| WondersOfTheAncientWorld.MausoleumAtHalicarnassus;
|
||
|
|
|
||
|
|
// bob.BucketList = (WondersOfTheAncientWorld)18;
|
||
|
|
|
||
|
|
WriteLine($"{bob.Name}'s bucket list is {bob.BucketList}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Storing multiple values using collections
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
bob.Children.Add(new Person { Name = "Alfred" });
|
||
|
|
bob.Children.Add(new Person { Name = "Zoe" });
|
||
|
|
|
||
|
|
WriteLine(
|
||
|
|
$"{bob.Name} has {bob.Children.Count} children:");
|
||
|
|
|
||
|
|
for (int childIndex = 0; childIndex < bob.Children.Count; childIndex++)
|
||
|
|
{
|
||
|
|
WriteLine($" {bob.Children[childIndex].Name}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Making a field static
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
public class BankAccount
|
||
|
|
{
|
||
|
|
public string AccountName; // instance member
|
||
|
|
public decimal Balance; // instance member
|
||
|
|
public static decimal InterestRate; // shared member
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
BankAccount.InterestRate = 0.012M; // store a shared value
|
||
|
|
|
||
|
|
var jonesAccount = new BankAccount();
|
||
|
|
jonesAccount.AccountName = "Mrs. Jones";
|
||
|
|
jonesAccount.Balance = 2400;
|
||
|
|
|
||
|
|
WriteLine(format: "{0} earned {1:C} interest.",
|
||
|
|
arg0: jonesAccount.AccountName,
|
||
|
|
arg1: jonesAccount.Balance * BankAccount.InterestRate);
|
||
|
|
|
||
|
|
var gerrierAccount = new BankAccount();
|
||
|
|
gerrierAccount.AccountName = "Ms. Gerrier";
|
||
|
|
gerrierAccount.Balance = 98;
|
||
|
|
|
||
|
|
WriteLine(format: "{0} earned {1:C} interest.",
|
||
|
|
arg0: gerrierAccount.AccountName,
|
||
|
|
arg1: gerrierAccount.Balance * BankAccount.InterestRate);
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Making a field constant
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine($"{bob.Name} is a {Person.Species}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Making a field read-only
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine($"{bob.Name} was born on {bob.HomePlanet}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Initializing fields with constructors
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
var blankPerson = new Person();
|
||
|
|
|
||
|
|
WriteLine(format:
|
||
|
|
"{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.",
|
||
|
|
arg0: blankPerson.Name,
|
||
|
|
arg1: blankPerson.HomePlanet,
|
||
|
|
arg2: blankPerson.Instantiated);
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Defining multiple constructors
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
var gunny = new Person("Gunny", "Mars");
|
||
|
|
|
||
|
|
WriteLine(format:
|
||
|
|
"{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.",
|
||
|
|
arg0: gunny.Name,
|
||
|
|
arg1: gunny.HomePlanet,
|
||
|
|
arg2: gunny.Instantiated);
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Returning values from methods
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
bob.WriteToConsole();
|
||
|
|
WriteLine(bob.GetOrigin());
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Combining multiple returned values using tuples
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
public class TextAndNumber
|
||
|
|
{
|
||
|
|
public string Text;
|
||
|
|
public int Number;
|
||
|
|
}
|
||
|
|
|
||
|
|
public class LifeTheUniverseAndEverything
|
||
|
|
{
|
||
|
|
public TextAndNumber GetTheData()
|
||
|
|
{
|
||
|
|
return new TextAndNumber
|
||
|
|
{
|
||
|
|
Text = "What's the meaning of life?",
|
||
|
|
Number = 42
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
### Language support for tuples
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
(string, int) fruit = bob.GetFruit();
|
||
|
|
|
||
|
|
WriteLine($"{fruit.Item1}, {fruit.Item2} there are.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
### Naming the fields of a tuple
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
var fruitNamed = bob.GetNamedFruit();
|
||
|
|
|
||
|
|
WriteLine($"There are {fruitNamed.Number} {fruitNamed.Name}.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
### Inferring tuple names
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
var thing1 = ("Neville", 4);
|
||
|
|
WriteLine($"{thing1.Item1} has {thing1.Item2} children.");
|
||
|
|
|
||
|
|
var thing2 = (bob.Name, bob.Children.Count);
|
||
|
|
WriteLine($"{thing2.Name} has {thing2.Count} children.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
### Deconstructing tuples
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
(string fruitName, int fruitNumber) = bob.GetFruit();
|
||
|
|
|
||
|
|
WriteLine($"Deconstructed: {fruitName}, {fruitNumber}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Defining and passing parameters to methods
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine(bob.SayHello());
|
||
|
|
WriteLine(bob.SayHello("Emily"));
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Passing optional and named parameters
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine(bob.OptionalParameters());
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine(bob.OptionalParameters("Jump!", 98.5));
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine(bob.OptionalParameters(
|
||
|
|
number: 52.7, command: "Hide!"));
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine(bob.OptionalParameters("Poke!", active: false));
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Controlling how parameters are passed
|
||
|
|
|
||
|
|
When a parameter is passed into a method, it can be passed in one of three ways:
|
||
|
|
- By value (this is the default): Think of these as being in-only.
|
||
|
|
- By reference as a ref parameter: Think of these as being in-and-out.
|
||
|
|
- As an out parameter: Think of these as being out-only.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
int a = 10;
|
||
|
|
int b = 20;
|
||
|
|
int c = 30;
|
||
|
|
|
||
|
|
WriteLine($"Before: a = {a}, b = {b}, c = {c}");
|
||
|
|
bob.PassingParameters(a, ref b, out c);
|
||
|
|
WriteLine($"After: a = {a}, b = {b}, c = {c}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Simplified out parameters
|
||
|
|
|
||
|
|
In C# 7.0 and later, we can simplify code that uses the out variables.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
int d = 10;
|
||
|
|
int e = 20;
|
||
|
|
|
||
|
|
WriteLine($"Before: d = {d}, e = {e}, f doesn't exist yet!");
|
||
|
|
|
||
|
|
// simplified C# 7.0 or later syntax for the out parameter
|
||
|
|
bob.PassingParameters(d, ref e, out int f);
|
||
|
|
WriteLine($"After: d = {d}, e = {e}, f = {f}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Splitting classes using partial
|
||
|
|
|
||
|
|
Partial classes are not supported in .NET Interactive notebooks. I have added the code to the original class near the top of this notebook.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
Person sam = new()
|
||
|
|
{
|
||
|
|
Name = "Sam",
|
||
|
|
DateOfBirth = new(1972, 1, 27)
|
||
|
|
};
|
||
|
|
WriteLine(sam.Origin);
|
||
|
|
WriteLine(sam.Greeting);
|
||
|
|
WriteLine(sam.Age);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
sam.FavoriteIceCream = "Chocolate Fudge";
|
||
|
|
|
||
|
|
WriteLine($"Sam's favorite ice-cream flavor is {sam.FavoriteIceCream}.");
|
||
|
|
|
||
|
|
sam.FavoritePrimaryColor = "Red";
|
||
|
|
|
||
|
|
WriteLine($"Sam's favorite primary color is {sam.FavoritePrimaryColor}.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Defining indexers
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
sam.Children.Add(new() { Name = "Charlie" });
|
||
|
|
sam.Children.Add(new() { Name = "Ella" });
|
||
|
|
|
||
|
|
WriteLine($"Sam's first child is {sam.Children[0].Name}");
|
||
|
|
WriteLine($"Sam's second child is {sam.Children[1].Name}");
|
||
|
|
|
||
|
|
WriteLine($"Sam's first child is {sam[0].Name}");
|
||
|
|
WriteLine($"Sam's second child is {sam[1].Name}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Pattern matching with objects
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
public class BusinessClassPassenger
|
||
|
|
{
|
||
|
|
public override string ToString()
|
||
|
|
{
|
||
|
|
return $"Business Class";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class FirstClassPassenger
|
||
|
|
{
|
||
|
|
public int AirMiles { get; set; }
|
||
|
|
|
||
|
|
public override string ToString()
|
||
|
|
{
|
||
|
|
return $"First Class with {AirMiles:N0} air miles";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class CoachClassPassenger
|
||
|
|
{
|
||
|
|
public double CarryOnKG { get; set; }
|
||
|
|
|
||
|
|
public override string ToString()
|
||
|
|
{
|
||
|
|
return $"Coach Class with {CarryOnKG:N2} KG carry on";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
object[] passengers = {
|
||
|
|
new FirstClassPassenger { AirMiles = 1_419 },
|
||
|
|
new FirstClassPassenger { AirMiles = 16_562 },
|
||
|
|
new BusinessClassPassenger(),
|
||
|
|
new CoachClassPassenger { CarryOnKG = 25.7 },
|
||
|
|
new CoachClassPassenger { CarryOnKG = 0 },
|
||
|
|
};
|
||
|
|
|
||
|
|
foreach (object passenger in passengers)
|
||
|
|
{
|
||
|
|
decimal flightCost = passenger switch
|
||
|
|
{
|
||
|
|
/*
|
||
|
|
FirstClassPassenger p when p.AirMiles > 35000 => 1500M,
|
||
|
|
FirstClassPassenger p when p.AirMiles > 15000 => 1750M,
|
||
|
|
FirstClassPassenger _ => 2000M, */
|
||
|
|
|
||
|
|
// C# 9 or later syntax
|
||
|
|
FirstClassPassenger p => p.AirMiles switch
|
||
|
|
{
|
||
|
|
> 35000 => 1500M,
|
||
|
|
> 15000 => 1750M,
|
||
|
|
_ => 2000M
|
||
|
|
},
|
||
|
|
|
||
|
|
|
||
|
|
BusinessClassPassenger => 1000M,
|
||
|
|
CoachClassPassenger p when p.CarryOnKG < 10.0 => 500M,
|
||
|
|
CoachClassPassenger => 650M,
|
||
|
|
_ => 800M
|
||
|
|
};
|
||
|
|
|
||
|
|
WriteLine($"Flight costs {flightCost:C} for {passenger}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with records
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Init-only properties
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
|
||
|
|
public class ImmutablePerson
|
||
|
|
{
|
||
|
|
public string? FirstName { get; init; }
|
||
|
|
public string? LastName { get; init; }
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
ImmutablePerson jeff = new()
|
||
|
|
{
|
||
|
|
FirstName = "Jeff",
|
||
|
|
LastName = "Winger"
|
||
|
|
};
|
||
|
|
jeff.FirstName = "Geoff";
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Understanding records
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
|
||
|
|
public record ImmutableVehicle
|
||
|
|
{
|
||
|
|
public int Wheels { get; init; }
|
||
|
|
public string? Color { get; init; }
|
||
|
|
public string? Brand { get; init; }
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
ImmutableVehicle car = new()
|
||
|
|
{
|
||
|
|
Brand = "Mazda MX-5 RF",
|
||
|
|
Color = "Soul Red Crystal Metallic",
|
||
|
|
Wheels = 4
|
||
|
|
};
|
||
|
|
|
||
|
|
ImmutableVehicle repaintedCar = car
|
||
|
|
with { Color = "Polymetal Grey Metallic" };
|
||
|
|
|
||
|
|
WriteLine($"Original car color was {car.Color}.");
|
||
|
|
WriteLine($"New car color is {repaintedCar.Color}.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Simplifying data members in records
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
// simpler way to define a record
|
||
|
|
// auto-generates the properties, constructor, and deconstructor
|
||
|
|
public record ImmutableAnimal(string Name, string Species);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
ImmutableAnimal oscar = new("Oscar", "Labrador");
|
||
|
|
var (who, what) = oscar; // calls Deconstruct method
|
||
|
|
WriteLine($"{who} is a {what}.");
|