Skip to content

Commit 19ff204

Browse files
committed
grrr
1 parent 71898bb commit 19ff204

File tree

3 files changed

+199
-0
lines changed

3 files changed

+199
-0
lines changed

docs/document/Articles/docs/Duck Typing in CSharp.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@ static class FooExtension
2323
public static void Deconstruct(this Foo self, out object? a, out object? b) => a = b = null;
2424
}
2525
```
26+
27+
If you do want the type has an overall solution for enumeration, just implement a `GetEnumerator` method.
28+
`foreach` and `GetEnumerator` now supports duck typing, meaning that:
29+
30+
- An object implemented `GetEnumerator` can be enumerated by `foreach`
31+
- The return type of `GetEnumerator` can be valid as long as it satisfies the shape of `IEnumerator<T>`
32+
33+
This feature is typically for `ref struct`.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# String
2+
3+
# Native String
4+
5+
Strings in bash can be native without any quote.
6+
7+
```sh
8+
name=john_smith # it's a string without quotes
9+
```
10+
11+
## Raw String
12+
13+
Use single quotes to represent a raw string.
14+
All escapes are treated as literal.
15+
16+
```sh
17+
script='
18+
cd $(ls ~/projects/* | fzf)
19+
echo hello
20+
'
21+
```
22+
23+
> [!NOTE]
24+
> You can't interpolate in single quote string
25+
26+
## Interpolated String
27+
28+
Use double quotes to represent interpolated string.
29+
30+
```sh
31+
first_name=john
32+
last_name=smith
33+
fullname="$first_name $last_name"
34+
```
35+
36+
The best practice is using the complete `${}` to distinguish between.
37+
38+
```sh
39+
number=123
40+
echo "$number456" # foo123 not avaiable in the context
41+
echo "${name}456" # use this instead!
42+
```
43+
44+
## String Concatenation
45+
46+
Strings can be concatenated directly side by side except spaces.
47+
48+
```sh
49+
first_name=john
50+
last_name=smith
51+
fullname=${first_name}${last_name} # johnsmith # [!code highlight]
52+
fullname=${first_name}" "$last_name # john smith # [!code highlight]
53+
fullname=$first_name $last_name # error: command not found # [!code error]
54+
```
55+
56+
57+
### `+=` Operator
58+
59+
toggleterm disabled, use normal nvim to test it today.
60+
61+
```sh
62+
foo=abc
63+
foo+=def # abcdef
64+
```
65+
66+
> [!WARNING]
67+
> `+=` will do numeric operation when two operands are both valid numeric strings.
68+
69+
> [!NOTE]
70+
> Always prefer interpolation over concatenation.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Iterator
2+
3+
## Motivation
4+
5+
Knows how to traverse over an object
6+
- Keep track of current element.
7+
- Knows how to move to next object.
8+
9+
## Iterator as an Object
10+
11+
The classic approach of an Iterator is making it an object for the enumeration of certain type.
12+
Since we're using .NET, we'll implement it with `IEnumerator` and `IEnumerator<T>`.
13+
14+
Let's take a look of `IEnumerator`:
15+
16+
```cs
17+
public interface IEnumerator
18+
{
19+
object Current { get; }
20+
bool MoveNext(); // change state and tell whether can move to next item.
21+
void Reset(); // reset to initial state before iteration.
22+
}
23+
```
24+
25+
Back in the early days when `C#` doesn't have generic types, `IEnumerator` was designed as a reconciled way.
26+
27+
Later we got `IEnumerator<T>` which simply overrides `Current` with concrete type.
28+
29+
```cs
30+
public interface IEnumerator<out T> : IEnumerator, IDisposable
31+
{
32+
T Current { get; }
33+
}
34+
```
35+
36+
> [!NOTE]
37+
> `IEnumerator<T>` extends `IDisposable` for making sure the object being iterated can be collected by GC later on.
38+
39+
```cs
40+
int[] foo = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
41+
42+
ArrayEnumerator<int> enumerator = new(foo);
43+
44+
while (enumerator.MoveNext())
45+
{
46+
Console.WriteLine(enumerator.Current);
47+
}
48+
49+
class ArrayEnumerator<T> : IEnumerator<T>
50+
{
51+
private readonly T[] _source = [];
52+
private int _cursor;
53+
public ArrayEnumerator(T[] source)
54+
{
55+
_source = source;
56+
Current = source[0];
57+
}
58+
59+
public T Current { get; private set; }
60+
61+
object IEnumerator.Current => Current;
62+
63+
64+
public bool MoveNext()
65+
{
66+
if (_cursor < _source.Length)
67+
{
68+
Current = _source[_cursor++];
69+
return true;
70+
}
71+
return false;
72+
}
73+
74+
public void Reset()
75+
{
76+
_cursor = 0;
77+
}
78+
79+
public void Dispose() { }
80+
}
81+
```
82+
83+
> [!tip]
84+
> It'e pretty ahrd to implement recursive logic in an iterator object, use iterator method instead if you do want it.
85+
86+
## Iterator as a Method
87+
88+
.NET can auto generates state machine for each method yield returns `IEnumerable<T>`.
89+
90+
```cs
91+
using System.Collections;
92+
93+
int[] foo = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
94+
foreach (var item in EnumerateArray(foo))
95+
{
96+
Console.WriteLine(item);
97+
}
98+
// pointless but that's ok
99+
static IEnumerable<T> EnumerateArray<T>(T[] source)
100+
{
101+
for (int i = 0; i < source.Length; i++)
102+
yield return source[i];
103+
}
104+
```
105+
106+
> [!TIP]
107+
> You can have multiple getter as Iterator methods, representing different ways of enumeration.
108+
109+
If you do want the type has an overall solution for enumeration, just implement a `GetEnumerator` method.
110+
`foreach` and `GetEnumerator` now supports duck typing, meaning that:
111+
112+
- An object implemented `GetEnumerator` can be enumerated by `foreach`
113+
- The return type of `GetEnumerator` can be valid as long as it satisfies the shape of `IEnumerator<T>`
114+
115+
This feature is typically for `ref struct`.
116+
117+
## Conclusion
118+
119+
- Use `IEnumerator<T>` to implement an Iterator class.
120+
- Use `IEnumerable<T>` as return type for Iterator methods.
121+
- Implement `GetEnumerator` to mark a the type to handle iteration by itself.

0 commit comments

Comments
 (0)