Steve Love
@IAmSteveLove@mastodon.social
@stevelove.bsky.social

Steve Love
@IAmSteveLove@mastodon.social
@stevelove.bsky.social
Who are you?
I hope you learn something interesting!
I want to mess with your mind! (and I hope you still learn something interesting)
Basic arithmetic
Every problem becomes very childish when once it is explained to you.
double price = 200.0;
double percent = 20.0;
price *= 1 + percent / 100.0;What does this do?
For a binary operator op, a compound assignment expression of the form
x op= yis equivalent to
x = x op yexcept that x is only evaluated once.
double a = 200.0;
double b = 0.2;
a = a * b;
Assert.That(a, Is.EqualTo(40));double a = 200.0;
double b = 0.2;
a *= b;
Assert.That(a, Is.EqualTo(40));double price = 200.0;
double percent = 20.0;
price *= 1 + percent / 100.0;(a) | |
|
(b) | |
|

double price = 200.0;
double percent = 20.0;
price *= 1 + percent / 100.0;
Assert.That(price, Is.EqualTo(240));(a) | |
|
But…why?
Operators | Category or name |
blah… | blah… |
| Multiplicative |
| Additive |
blah… | blah… |
| Assignment and lambda declaration |
| |
| 240.0 |
It matters!
It is a capital mistake to theorize before one has data.
| |
var child = new Child();
Console.WriteLine(child.Name);a)
| b)
| c)
|
| |
var child = new Child();
Console.WriteLine(child.Name);a)
| b)
| c) |


![]() | |
var child = new Child();
Console.WriteLine(child.Name);a)
| b)
| c)
|
![]() | |
var child = new Child();
Console.WriteLine(child.Name);a)
| b) | c)
|
With a (virtual) set accessor:
IL_0013: ldarg.0 // this
IL_0014: ldarg.1 // name
IL_0015: callvirt instance void code.Test_auto_mutable_prop/Base::set_Name(string)
IL_001a: nop Without a set accessor:
IL_0013: ldarg.0 // this
IL_0014: ldarg.1 // name
IL_0015: stfld string code.Test_auto_immutable_prop/Base::'<Name>k__BackingField'but it’s just minutiae, right?
I never make exceptions. An exception disproves the rule.
…return value
| |
Your program might exit unconditionally
i.e. it’ll crash
probably
Warning: high-level hand waving here!
A function that returns a future
A a result that may be obtained on a different thread asynchronously, and made available to the current thread by a promise
asynchronous exceptions are…tricksy
An exception is a result!
See 1
async doesn’t make a method asynchronousactually, await is where any asynchrony happens
The Synchronization Context decides
The future is Task
public async Task PersistState() // <-- async Task
{
await Task.Run(() =>
{
// long-running operation
// ... that may fail
throw new Exception("An arbitrary error");
});
}But…
the exception is still not caught in the caller
async all the way downpublic async void Flush(object? state) // <-- async
{
try
{
await PersistState(); // <-- await
Console.WriteLine("Done");
}
catch // anything
{
Console.WriteLine("Error");
}
}But…we’re back to async void!
There is nothing more deceptive than an obvious fact.
requiredpublic readonly struct Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public DateOnly DOB { get; init; }
} var person = new Person
{
FirstName = "John",
LastName = "Doe"
};public readonly struct Person
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required DateOnly DOB { get; init; }
} 
var person = new Person
{
FirstName = "John",
LastName = "Doe"
};Error CS9035 : Required member 'Person.DOB' must be set in the
object initializer or attribute constructor.
var person = new Person
{
FirstName = "John",
LastName = default,
DOB = default
};[CS8625] Cannot convert null literal to non-nullable reference type.
…but silent on the default DOB
Person person = default;| Consider making this a sin! |
public readonly struct Person
{
public Person(string firstName, string lastName, DateOnly dob)
=> (FirstName, LastName, DOB) = (firstName, lastName, dob);
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required DateOnly DOB { get; init; }
}
var person = new Person("John", "Doe", new DateOnly(2010, 12, 31));[CS9035] Required member 'Person.FirstName' must be set
in the object initializer or attribute constructor.public readonly struct Person
{
[SetsRequiredMembers]
public Person(string firstName, string lastName, DateOnly dob)
=> (FirstName, LastName, DOB) = (firstName, lastName, dob);
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required DateOnly DOB { get; init; }
public required string Pronoun { get; init; }
}
var person = new Person("John", "Doe", new DateOnly(2010, 12, 31));Should this be ok?
public readonly struct Person
{
public Person(string firstName, string lastName, DateOnly dob, string pronoun)
=> (FirstName, LastName, DOB, Pronoun) = (firstName, lastName, dob, pronoun);
public string FirstName { get; }
public string LastName { get; }
public DateOnly DOB { get; }
public string Pronoun { get; }
}
var person = new Person { FirstName = "John", LastName = "Doe" };[CS0200] Property or indexer 'Test_construct.Person.FirstName'
cannot be assigned to -- it is read onlyWe must look for consistency. Where there is a want of it we must suspect deception.
public class Converter(char from)
{
public static explicit operator Converter(char value)
=> new(value);
}decimal money = 24.5m + 11.5m;
var result = (Converter)money;Does this compile?
Should it?
decimal money = 24.5m + 11.5m;
var result = (Converter)money;is equivalent to this:
var result = Converter.op_Explicit(money);public class Converter(char from)
{
public static explicit operator Converter(char value)
=> new(value);
public static Converter From(char value)
=> new(value);
}
var result = Converter.From(money);
var result = Converter.From((char)money);decimal money = 24.5m + 11.5m;
var result = (Converter)money;IL_0012: call char System.Decimal::op_Explicit(valuetype System.Decimal)
IL_0017: call class Converter Converter::op_Explicit(char)
First, if required, performing a standard conversion from the source expression to the operand type of the user-defined or lifted conversion operator.
Next, invoking the user-defined or lifted conversion operator to perform the conversion.
Finally, if required, performing a standard conversion from the result type of the user-defined conversion operator to the target type.
It is, of course, a trifle, but there is nothing so important as trifles.