From b95dac6d9e1a346290a0c8ece9d4d6c8827d06c8 Mon Sep 17 00:00:00 2001 From: sharpchen Date: Sun, 10 Nov 2024 00:54:36 +0800 Subject: [PATCH] grrr --- .../Articles/docs/Duck Typing in CSharp.md | 25 ++++ .../docs/2. Styling/AXAML/Adding New Style.md | 51 +++++++ .../docs/2. Styling/AXAML/Importing Styles.md | 19 +++ .../docs/2. Styling/Selector Syntax.md | 7 - .../docs/2. Styling/Style Isolation.md | 45 ------ .../MVVM Essentials/4. Trigger a Command.md | 136 +++++++++++++++++- .../docs/Behavioural/Interpreter.md | 5 +- .../docs/Powershell as a Language/Function.md | 57 ++++++++ docs/services/DocumentService.ts | 9 +- docs/services/SidebarService.ts | 2 +- tsconfig.json | 3 +- 11 files changed, 299 insertions(+), 60 deletions(-) create mode 100644 docs/document/Articles/docs/Duck Typing in CSharp.md create mode 100644 docs/document/Avalonia/docs/2. Styling/AXAML/Adding New Style.md create mode 100644 docs/document/Avalonia/docs/2. Styling/AXAML/Importing Styles.md delete mode 100644 docs/document/Avalonia/docs/2. Styling/Selector Syntax.md delete mode 100644 docs/document/Avalonia/docs/2. Styling/Style Isolation.md create mode 100644 docs/document/Powershell/docs/Powershell as a Language/Function.md diff --git a/docs/document/Articles/docs/Duck Typing in CSharp.md b/docs/document/Articles/docs/Duck Typing in CSharp.md new file mode 100644 index 00000000..33a797a8 --- /dev/null +++ b/docs/document/Articles/docs/Duck Typing in CSharp.md @@ -0,0 +1,25 @@ +# Duck Typing in CSharp + +Currently only `GetEnumerator` and `Deconstruct` supports duck typing. +`Dispose` is just not available since you the compiler won't be able to know which one to pick if there's duplication. + +```cs +using System.Collections; + +using var foo = new Foo(); // [!code error] +foreach (var _ in new Foo()) ; +var (a, b) = new Foo(); + +class Foo; + +static class FooExtension +{ + public static void Dispose(this Foo self) => Console.WriteLine("Disposing Foo"); + public static IEnumerator GetEnumerator(this Foo _) + { + for (int i = 0; i < 100; i++) + yield return i; + } + public static void Deconstruct(this Foo self, out object? a, out object? b) => a = b = null; +} +``` diff --git a/docs/document/Avalonia/docs/2. Styling/AXAML/Adding New Style.md b/docs/document/Avalonia/docs/2. Styling/AXAML/Adding New Style.md new file mode 100644 index 00000000..a04b70c2 --- /dev/null +++ b/docs/document/Avalonia/docs/2. Styling/AXAML/Adding New Style.md @@ -0,0 +1,51 @@ +# Adding New Style + +## With dotnet cli + +```sh +dotnet new avalonia.styles -o Styles -n NewStyle +``` + +## Default Template + +Style template ships with a portion for previewer where you can add testing controls to see whether those styles works properly. + +```xml + + + + + + + + + +``` + +## Define a Style + +A style starts with a `Selector` which is the rule of target matching, for figuring out which control should have those contained styling. +Contained styling is some key-value pair on properties. + +> [!NOTE] +> See [selector syntax]() + +```xml + + + + + + + + + + + +``` diff --git a/docs/document/Avalonia/docs/2. Styling/AXAML/Importing Styles.md b/docs/document/Avalonia/docs/2. Styling/AXAML/Importing Styles.md new file mode 100644 index 00000000..23393f27 --- /dev/null +++ b/docs/document/Avalonia/docs/2. Styling/AXAML/Importing Styles.md @@ -0,0 +1,19 @@ +## Importing Styles + +```xml + + + + + + + + + +``` diff --git a/docs/document/Avalonia/docs/2. Styling/Selector Syntax.md b/docs/document/Avalonia/docs/2. Styling/Selector Syntax.md deleted file mode 100644 index 81994bf6..00000000 --- a/docs/document/Avalonia/docs/2. Styling/Selector Syntax.md +++ /dev/null @@ -1,7 +0,0 @@ -# Selector Syntax - -:::info -[Selector syntax](https://docs.avaloniaui.net/docs/reference/styles/style-selector-syntax) -::: - -## Code Behind diff --git a/docs/document/Avalonia/docs/2. Styling/Style Isolation.md b/docs/document/Avalonia/docs/2. Styling/Style Isolation.md deleted file mode 100644 index 79a8a6d3..00000000 --- a/docs/document/Avalonia/docs/2. Styling/Style Isolation.md +++ /dev/null @@ -1,45 +0,0 @@ -# Style Isolation - -A style in avalonia can be a single file and can be imported into a control. - -## Create a isolated style - -```bash -dotnet new avalonia.styles -n MyStyle -``` - -```xml{10-12} - - - - - - - - - - - -``` - -## Importing a style - -```xml{9} - - - - - - - - -``` diff --git a/docs/document/Avalonia/docs/MVVM Essentials/4. Trigger a Command.md b/docs/document/Avalonia/docs/MVVM Essentials/4. Trigger a Command.md index ce8a9e5b..990a5d89 100644 --- a/docs/document/Avalonia/docs/MVVM Essentials/4. Trigger a Command.md +++ b/docs/document/Avalonia/docs/MVVM Essentials/4. Trigger a Command.md @@ -1,3 +1,137 @@ # Trigger a Command -## RelayCommand +`CommunityToolkit.Mvvm` uses `RelayCommand` to generate a field and property to represents the command for certain method. + +## Code Emission + +```cs +[RelayCommand] +private void RemoveItem(ToDoItemViewModel item) +{ + ToDoItems.Remove(item); +} +``` + +Generated part as following: + +- A field of type `RelayCommand`(not the `RelayCommandAttribute`) +- A property for the generated field typed as `IRelayCommand`. + +```cs +/// The backing field for . +[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.3.0.0")] +private global::CommunityToolkit.Mvvm.Input.RelayCommand? removeItemCommand; // [!code highlight] +/// Gets an instance wrapping . +[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.3.0.0")] +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] +public global::CommunityToolkit.Mvvm.Input.IRelayCommand RemoveItemCommand => removeItemCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(RemoveItem)); // [!code highlight] +``` + +## Behind the `RelayCommand` + +To understand how `RelayCommand` works, we should go back to the story of `ICommand` first. + +`ICommand` is a builtin interface in .NET, has three parts to describe the command pattern: +- `Execute`: A real action for the command. +- `CanExecute`: Telling whether the command can be executed now. +- `CanExecuteChanged`: Informing when `CanExecute` evaluation changed. + +```cs +public interface ICommand +{ + /// Occurs when changes occur that affect whether or not the command should execute. + event EventHandler? CanExecuteChanged; + + /// Defines the method that determines whether the command can execute in its current state. + /// Data used by the command. If the command does not require data to be passed, this object can be set to . + /// + /// if this command can be executed; otherwise, . + bool CanExecute(object? parameter); + + /// Defines the method to be called when the command is invoked. + /// Data used by the command. If the command does not require data to be passed, this object can be set to . + void Execute(object? parameter); +} +``` + +`IRelayCommand` in `CommunityToolkit` simply extends the `ICommand` with a new notify method. +We do need a real implementation of how we inform on `CanExecuteChanged`, this is generally missing in .NET builtin event-driven interfaces. + +```cs +public interface IRelayCommand : ICommand +{ + /// + /// Notifies that the property has changed. // [!code highlight] + /// + void NotifyCanExecuteChanged(); +} +``` + +> [!NOTE] +> `IRelayCommand` and `RelayCommand` have generic versions and async versions. + +The type of generated field is `RelayCommand` which implements the `IRelayCommand`, this is essentially a base wrapper for the logic behind. + +```cs +public sealed partial class RelayCommand : IRelayCommand +{ + /// + /// The to invoke when is used. + /// + private readonly Action execute; + + /// + /// The optional action to invoke when is used. + /// + private readonly Func? canExecute; + + /// + public event EventHandler? CanExecuteChanged; + + /// + /// Initializes a new instance of the class that can always execute. + /// + /// The execution logic. + /// Thrown if is . + public RelayCommand(Action execute) + { + ArgumentNullException.ThrowIfNull(execute); + + this.execute = execute; + } + + /// + /// Initializes a new instance of the class. + /// + /// The execution logic. + /// The execution status logic. + /// Thrown if or are . + public RelayCommand(Action execute, Func canExecute) + { + ArgumentNullException.ThrowIfNull(execute); + ArgumentNullException.ThrowIfNull(canExecute); + + this.execute = execute; + this.canExecute = canExecute; + } + + /// + public void NotifyCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CanExecute(object? parameter) + { + return this.canExecute?.Invoke() != false; + } + + /// + public void Execute(object? parameter) + { + this.execute(); + } +} +``` diff --git a/docs/document/Csharp Design Patterns/docs/Behavioural/Interpreter.md b/docs/document/Csharp Design Patterns/docs/Behavioural/Interpreter.md index fe75d2d9..b16ff495 100644 --- a/docs/document/Csharp Design Patterns/docs/Behavioural/Interpreter.md +++ b/docs/document/Csharp Design Patterns/docs/Behavioural/Interpreter.md @@ -217,4 +217,7 @@ var tokens = Lex("(1 + 3) - (5 + 2)"); var operation = Parse(CollectionsMarshal.AsSpan(tokens)); Console.WriteLine(operation.Value); // -3 ``` -## ANTLR + +> [!TIP] +> Manually writing parsers are very tedious, use certain tools to parse your language instead. +> ANTLR, bison, yacc for example. diff --git a/docs/document/Powershell/docs/Powershell as a Language/Function.md b/docs/document/Powershell/docs/Powershell as a Language/Function.md new file mode 100644 index 00000000..bc9080f8 --- /dev/null +++ b/docs/document/Powershell/docs/Powershell as a Language/Function.md @@ -0,0 +1,57 @@ +# Function + +## Parameter + +There's no positional parameters, it's a table-like definition, can be specified with any order. +Parameters are wrapped inside a function block with `param(...)` + +```ps1 +function Foo { + param ( + [string]$foo + ) +} +``` + +### Default Parameter + +```ps1 +function Foo { + param ( + [string]$foo = "foo" + ) +} +``` + +### Flags + +Defining flags that represents a toggle needs a special type called `switch`. +`switch` has the same nature of `bool`, but `bool` parameter requires explicit assignment when the function being called. +While `switch` will remain `$false` when unspecified. + +```ps1 +function Foo { + param ( + [switch]$foo + [bool]$bar + ) +} + +# this is why we should use `switch` instead. +Foo -foo -bar $true # [!code highlight] +``` + +### Required Parameter + +All parameters are optional by default. Use `[Parameter(Mandatory=$true)]` to mark it as required. + +```ps1 +param ( + [Parameter(Mandatory=$true)] + [string]$RequiredName +) +``` + +## Lifetime + +- Function should be define before it was called. diff --git a/docs/services/DocumentService.ts b/docs/services/DocumentService.ts index a4cd0191..f5506e78 100644 --- a/docs/services/DocumentService.ts +++ b/docs/services/DocumentService.ts @@ -19,15 +19,16 @@ export const documentMap = { // VBA: { icon: '💩', description: 'VBA for excel' }, // Vue3: { icon: '⚡', description: '' }, 'Unsafe CSharp': { icon: '😎', description: 'Entering the danger zone...' }, - 'NeoVim ColorScheme Development': { - icon: '🎨', - description: 'Make your own nvim color scheme using lua.', - }, + // 'NeoVim ColorScheme Development': { + // icon: '🎨', + // description: 'Make your own nvim color scheme using lua.', + // }, Bash: { icon: '🐢', description: 'Shebang!' }, 'Regular Expression': { icon: '🐫', description: 'Memory lossss for every 6 months' }, Nix: { icon: '❄', description: 'Reproduce freedom' }, 'Entity Framework Core': { icon: '🗿', description: '' }, 'HTML & CSS': { icon: '😬', description: '' }, + Powershell: { icon: '🐚', description: '...' }, } as const satisfies DocumentInfo; export type DocumentName = keyof typeof documentMap; diff --git a/docs/services/SidebarService.ts b/docs/services/SidebarService.ts index 1d830ab9..c605974f 100644 --- a/docs/services/SidebarService.ts +++ b/docs/services/SidebarService.ts @@ -1,5 +1,5 @@ import { execSync } from 'node:child_process'; -import path from 'node:path'; +import path = require('node:path'); import type { DefaultTheme } from 'vitepress'; import { type DirectoryInfo, type FileInfo, Path, documentRoot } from '../shared/FileSystem'; import { type DocumentName, documentMap, documentService } from './DocumentService'; diff --git a/tsconfig.json b/tsconfig.json index 4091c061..85433a5c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,6 @@ "vitePressExtensions": [ ".md" ] - } + }, + "esModuleInterop": true }