用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Objective-Arts/lens-dist --skill type-systems命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | type-systems |
| description | Language design philosophy |
Anders Hejlsberg's core belief: Languages should be powerful yet approachable. Complexity should be opt-in. The simple case should be simple; advanced features available when needed.
"Make the easy things easy, and the hard things possible."
Every language feature should earn its place. If it makes common tasks harder or confuses developers, it shouldn't exist.
Types exist to catch errors and enable tooling—not to satisfy the compiler.
Progressive disclosure of complexity:
// Simple case: inference handles it
var name = "Alice";
var numbers = new[] { 1, 2, 3 };
// More control when needed
List<string> names = new();
// Full generic power when required
Dictionary<string, Func<Task<IEnumerable<int>>>> lookup = new();
The evolution:
Each step made the common case simpler while keeping full power available.
Hejlsberg admits null was a billion-dollar mistake—but fixing it in an existing language requires care.
The retrofit approach:
// Opt-in at project or file level
#nullable enable
// Existing code still works
string oldCode = null; // Warning, not error
// New code is safe
string newCode = "safe";
string? nullable = null;
// Gradual migration possible
Why not breaking change:
Prefer explicit, composable features over hidden behavior.
LINQ: Composition done right
// Each method is independent, composable
var result = items
.Where(x => x.IsActive)
.OrderBy(x => x.Name)
.Select(x => x.Id)
.Take(10);
// You understand each step
// You can rearrange, remove, add
// No hidden coupling
Compare to "magic" ORMs:
// Hidden behavior, hard to understand
var items = context.Items
.Include(x => x.Related) // What SQL does this generate?
.ThenInclude(x => x.Other) // N+1 query issue hidden?
.ToList(); // When does query execute?
Modern C# increasingly favors immutability.
Records (C# 9):
// Immutable by default
public record Person(string Name, int Age);
// Mutation through copying
var older = person with { Age = person.Age + 1 };
Init-only properties (C# 9):
public class Config
{
public string Endpoint { get; init; } // Set once, then readonly
public int Timeout { get; init; }
}
Required members (C# 11):
public class Order
{
public required string CustomerId { get; init; }
public required decimal Total { get; init; }
}
// Compiler enforces all required members are set
Express what you want, not how to get it.
Evolution:
// C# 1: Imperative, error-prone
if (obj is string)
{
string s = (string)obj;
if (s.Length > 0) { ... }
}
// C# 7: Basic patterns
if (obj is string s && s.Length > 0) { ... }
// C# 8: Switch expressions
var description = obj switch
{
string s when s.Length == 0 => "empty string",
string s => $"string: {s}",
int n when n < 0 => "negative",
int n => $"number: {n}",
null => "null",
_ => "unknown"
};
// C# 11: List patterns
var result = list switch
{
[] => "empty",
[var single] => $"one: {single}",
[var first, .., var last] => $"first: {first}, last: {last}"
};
Async/await transformed how we write concurrent code.
Before (callback hell):
client.GetAsync(url, response =>
{
response.ReadAsync(data =>
{
Process(data, result =>
{
callback(result);
});
});
});
After (async/await):
var response = await client.GetAsync(url);
var data = await response.ReadAsync();
var result = await Process(data);
return result;
Design principle: Async code should read like sync code. The complexity of continuations is handled by the compiler.
When a method is just a return, show it.
Evolution:
// C# 1: Verbose
public int Double(int x)
{
return x * 2;
}
// C# 6: Expression-bodied methods
public int Double(int x) => x * 2;
// Extended to properties, constructors, etc.
public string Name { get; }
public Person(string name) => Name = name;
public override string ToString() => $"Person: {Name}";
Every new C# version can compile old code. Breaking changes are extremely rare.
Why this matters:
Advanced features don't complicate simple code.
// You can write C# ignoring:
// - Spans and Memory<T>
// - ref structs
// - Unsafe code
// - Source generators
// - Advanced generic constraints
// Until you need them
The compiler should help you write correct code.
// Definite assignment analysis
int x;
if (condition)
x = 1;
Console.WriteLine(x); // Error: x might not be assigned
// Exhaustiveness checking
int result = shape switch
{
Circle c => c.Area,
Rectangle r => r.Area,
// Warning: pattern not exhaustive
};
// Nullability analysis
string? name = GetName();
Console.WriteLine(name.Length); // Warning: possible null
When designing APIs or writing code, ask:
// Fields can't evolve
public string Name; // Can't add validation later without breaking
// Properties can
public string Name { get; set; } // Add logic later, no break
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException();
}
// Can't modify string class
// But can extend it
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string? s) => string.IsNullOrEmpty(s);
}
// Enables LINQ without modifying IEnumerable
// Enables method syntax on any type
var point = new { X = 1, Y = 2 }; // Anonymous type
// Can't be returned from methods
// Can't be passed as parameters (without generics)
// WHY: Forces named types for public contracts
| Scenario | Apply Hejlsberg |
|---|---|
| API design decisions | Yes - simplicity and evolution |
| Choosing between language features | Yes - what's idiomatic? |
| Understanding "why C# works this way" | Yes |
| Performance optimization | Partially - correctness first |
| Interop with other languages | Partially - C# idioms may not apply |
"Make the easy things easy, and the hard things possible." — Anders Hejlsberg