泛型方法
public static T First<T>(T[] arr)
{
return arr[0];
}
int n = First(new[] { 1, 2, 3 }); // T = int,推断
string s = First(new[] { "a", "b" }); // T = string
string s2 = First<string>(new[] { "a" }); // 显式指定
泛型类
public class Box<T>
{
public T Value { get; set; }
public Box(T value) { Value = value; }
}
var b1 = new Box<int>(42);
var b2 = new Box<string>("hi");
多类型参数
public class Pair<TKey, TValue>
{
public TKey Key { get; }
public TValue Value { get; }
public Pair(TKey k, TValue v) { Key = k; Value = v; }
}
var p = new Pair<string, int>("age", 30);
约束(where)
// T 必须是引用类型
public class Repo<T> where T : class { }
// T 必须是值类型
public class Container<T> where T : struct { }
// T 必须有无参构造函数
public T Create<T>() where T : new() => new T();
// T 必须实现某接口
public class Sorter<T> where T : IComparable<T>
{
public void Sort(List<T> items) { ... }
}
// T 必须继承某个类
public class AnimalShelter<T> where T : Animal { }
// 多约束(用 , 分隔)
public class X<T> where T : Animal, IComparable<T>, new() { }
约束让编译器知道你能拿 T 做什么——比如 where T : IComparable<T> 后才能调 T.CompareTo(...)。
无约束泛型
public class List<T> { } // 不要求 T 任何东西
只能用 object 上有的操作(.ToString()、.GetHashCode()、== 比引用)。
默认值
public static T? GetOrDefault<T>(T[] arr)
{
return arr.Length > 0 ? arr[0] : default;
}
default 关键字:
- 引用类型 →
null - 数值 →
0 - bool →
false - struct → 所有字段 default 化
类型推断
public static T Identity<T>(T x) => x;
Identity(5); // T = int
Identity("x"); // T = string
Identity<double>(5); // 显式指定(5 会被隐式转 double)
大多数情况下不用写 <T>——编译器从参数推断。
协变 / 逆变
协变(out):子类型可以替父类型:
IEnumerable<string> s = new List<string>();
IEnumerable<object> o = s; // ✅(string 是 object 的子类型)
逆变(in):父类型可以替子类型:
Action<object> a = (o) => Console.WriteLine(o);
Action<string> b = a; // ✅(接受 object 的也能接受 string)
只接口和委托上有 in / out,泛型类没有。99% 用户只是消费现有的,不需要自己声明。
泛型 vs 反射 vs dynamic
// 1. 泛型:编译期类型安全 + 零开销
T First<T>(IEnumerable<T> seq) => seq.First();
// 2. 反射:运行期检查,慢、易出错
object First(object seq) { return seq.GetType().GetMethod("First").Invoke(seq, null); }
// 3. dynamic:运行期解析,丧失类型检查
dynamic First2(dynamic seq) => seq.First();
永远首选泛型。反射 / dynamic 是特殊场景兜底。
例:通用结果类型
public class Result<T>
{
public T? Value { get; }
public string? Error { get; }
public bool IsSuccess => Error is null;
private Result(T? value, string? error) { Value = value; Error = error; }
public static Result<T> Ok(T value) => new(value, null);
public static Result<T> Fail(string error) => new(default, error);
}
// 用
public Result<User> GetUser(int id)
{
var user = db.Find(id);
if (user is null) return Result<User>.Fail("not found");
return Result<User>.Ok(user);
}
var r = GetUser(1);
if (r.IsSuccess) Console.WriteLine(r.Value!.Name);
else Console.WriteLine(r.Error);
泛型集合(下一篇细讲)
List<T> // 数组式
Dictionary<K, V> // 哈希字典
HashSet<T> // 集合
Queue<T> // 队列
Stack<T> // 栈
LinkedList<T> // 双向链表
永远用泛型版本——不要碰 ArrayList / Hashtable(.NET Framework 时代的非泛型 API)。
→ 下一篇 集合