什么是泛型?
- 定义:泛型允许您在类、接口和方法中定义占位符,这些占位符在使用时可以指定为具体的类型。
- 作用:通过减少重复代码和提供更强的类型检查,提高了代码的可重用性和性能。
泛型的核心概念
1.泛型类
- 泛型类能够操作特定的数据类型,而不需要为每种数据类型写一个类。提高代码复用性。
using System;
using System.Collections.Generic;
public class GenericContainer<T>
{
private T item;
// 设置泛型项的值
public void SetItem(T value)
{
item = value;
}
// 获取泛型项的值
public T GetItem()
{
return item;
}
}
使用泛型类示例:
public static void Demo()
{
// 使用int类型
var intContainer = new GenericContainer<int>();
intContainer.SetItem(10);
// 使用string类型
var stringContainer = new GenericContainer<string>();
stringContainer.SetItem("Hello");
}
2.泛型方法
可以在非泛型类中定义泛型方法,允许该方法独立于其所在类的参数类型。
public class GenericMethods
{
// ref关键字表示参数按引用传递
public void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
// 演示多个类型参数的泛型方法
public TResult Convert<TInput, TResult>(TInput input) where TResult : new()
{
// 处理类型转换逻辑
return new TResult();
}
}
使用泛型方法的示例:
public static void Demo()
{
var methods = new GenericMethods();
// 演示Swap方法
int x = 10, y = 20;
Console.WriteLine($"交换前: x = {x}, y = {y}");
methods.Swap(ref x, ref y);
Console.WriteLine($"交换后: x = {x}, y = {y}");
string str1 = "Hello", str2 = "World";
Console.WriteLine($"交换前: str1 = {str1}, str2 = {str2}");
methods.Swap(ref str1, ref str2);
Console.WriteLine($"交换后: str1 = {str1}, str2 = {str2}");
// 演示Convert方法
int number = 42;
string result = methods.Convert<int, string>(number);
Console.WriteLine($"转换结果: {result}");
}
3.泛型接口
允许接口的方法和属性使用泛型类型参数。
public interface IRepository<T>
{
void Add(T item);
void Remove(T item);
T GetById(int id);
IEnumerable<T> GetAll();
}
使用示例:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
// 实现用户仓储类
public class UserRepository : IRepository<User>
{
private List<User> users = new List<User>();
public void Add(User item)
{
users.Add(item);
}
public void Remove(User item)
{
users.Remove(item);
}
public User GetById(int id)
{
return users.FirstOrDefault(u => u.Id == id);
}
public IEnumerable<User> GetAll()
{
return users;
}
}
// 演示仓储接口的使用
public static void Demo()
{
var userRepo = new UserRepository();
// 添加用户
userRepo.Add(new User { Id = 1, Name = "张三" });
userRepo.Add(new User { Id = 2, Name = "李四" });
// 获取所有用户
var allUsers = userRepo.GetAll();
foreach (var user in allUsers)
{
Console.WriteLine($"用户ID: {user.Id}, 名称: {user.Name}");
}
// 根据ID获取用户
var user1 = userRepo.GetById(1);
Console.WriteLine($"查找到用户: {user1?.Name}");
// 删除用户
userRepo.Remove(user1);
}
高级主题
约束
限制泛型参数的类型以增加灵活性和安全性。
- where T : struct:T必须是值类型。
- where T : class:T必须是引用类型。
- where T : new():T必须有一个无参构造函数。
- where T : BaseClass:T必须继承自BaseClass。
- where T : InterfaceName:T必须实现某接口。
public class Example<T> where T : new()
{
public T CreateInstance()
{
return new T();
}
}
public class GenericConstraints
{
public T Sum<T>(T a, T b) where T : struct
}
泛型委托
泛型不仅适用于类和方法,还可以用于委托。
public delegate T Transformer<T>(T arg);
class Program
{
static int Square(int x) => x * x;
static void Main()
{
Transformer<int> transformer = Square;
Console.WriteLine(transformer(3)); // 输出:9
}
}
使用场景
1.集合类
- List<T>, Dictionary<TKey,TValue>, Queue<T>等都是泛型类的例子,可存储任何类型数据。
2.算法实现
- 可以创建通用的排序、搜索或其他算法,适用于任何类型。
3.类型安全事件处理
- 泛型委托用于事件系统中,确保类型匹配并提高安全性。
实践习题
1.创建一个泛型栈类GenericStack<T>,实现基本的栈操作:Push、Pop和Peek。
using System;
using System.Collections.Generic;
public class GenericStack<T>
{
private List<T> elements = new List<T>();
// 将元素添加到栈顶的Push操作
public void Push(T item)
{
elements.Add(item);
}
// 移除并返回栈顶元素的Pop操作
public T Pop()
{
if (elements.Count == 0)
{
throw new InvalidOperationException("The stack is empty.");
}
T item = elements[^1]; // ^1 是C# 8.0语法,用于访问最后一个元素
elements.RemoveAt(elements.Count - 1);
return item;
}
// 返回栈顶元素但不移除的Peek操作
public T Peek()
{
if (elements.Count == 0)
{
throw new InvalidOperationException("The stack is empty.");
}
return elements[^1];
}
}
public class Program
{
public static void Main()
{
GenericStack<int> stack = new GenericStack<int>();
stack.Push(10);
stack.Push(20);
stack.Push(30);
Console.WriteLine(stack.Peek()); // 输出:30
Console.WriteLine(stack.Pop()); // 输出:30
Console.WriteLine(stack.Pop()); // 输出:20
}
}
2.编写一个泛型类LimitedType<T>,仅允许实现了IDisposable接口的类型作为参数,并包含一个释放资源的方法。
using System;
public class LimitedType<T> where T : IDisposable, new()
{
private T resource;
public LimitedType()
{
resource = new T();
}
public void UseResource()
{
Console.WriteLine($"Using resource of type {typeof(T).Name}");
// 假装使用资源
}
public void ReleaseResource()
{
Console.WriteLine($"Releasing resource of type {typeof(T).Name}");
resource.Dispose();
}
}
public class MyResource : IDisposable
{
public void Dispose()
{
Console.WriteLine("MyResource disposed");
}
}
public class Program
{
public static void Main()
{
LimitedType<MyResource> limitedResource = new LimitedType<MyResource>();
limitedResource.UseResource();
limitedResource.ReleaseResource();
}
}
说明:
- LimitedType类使用where T : IDisposable, new()约束,这意味着T必须实现IDisposable接口,并且具有无参构造函数。
- UseResource模拟使用资源,而ReleaseResource负责正确地释放资源。
- MyResource类实现了IDisposable接口,用于演示如何正确地处置资源。
通过这些例子,我们展示了如何利用泛型提高代码的通用性和灵活性。如果有任何问题或需要进一步讲解,请随时告诉我!