当前位置: 首页 > news >正文

.NET 泛型编程(泛型类、泛型方法、泛型接口、泛型委托、泛型约束)

文章目录

  • 前言
  • 什么是泛型编程?
  • 泛型编程优点
  • 泛型类
  • 泛型方法
  • 泛型接口
  • 泛型委托
  • 泛型约束
    • 泛型约束的使用场景
    • 类约束
    • 接口约束
    • 构造函数约束
    • 值类型约束
    • 引用类型约束


前言

转载: 方程式sunny

视频教程: 跟着sunny老师学C#

源码: gitee仓库


什么是泛型编程?


  • 泛型编程(Generic Programming)是一种编程范式,核心思想是将数据类型参数化,使代码能够在不针对特定类型的情况下编写,从而实现 “一套逻辑,多类型适配”。
  • 通俗来说,就是编写可以重复使用的代码,以适用多种类型的数据。
  • 假设你写了一个函数来处理整数,然后又写了一个几乎一模一样的函数来处理字符串,这样的代码既冗余又难以维护,而泛型编程就能很好的解决这个问题,你只需编写一次代码,就可以让它适用于多种不同的数据类型。

泛型编程优点


  • 减少重复代码:不用为每种数据类型编写一套代码,减少了代码量,也降低了出错的风险。
  • 提高代码的通用性:一段泛型代码可以处理多种类型的数据,让代码更具通用性。
  • 增强代码的安全性:使用泛型时,编译器可以检查类型是否正确,这样可以避免很多潜在的错误。
  • 优化性能:泛型编程在编译时会生成针对具体类型的代码,没有装箱和拆箱的过程,不会影响运行时的性能。

泛型类

泛型类是在类名后面加上尖括号 <T>,其中 T 是类型参数,可以是任何符号或字母,代表类可以操作的某种类型。

// 实例化泛型类并使用
var intInstance = new GenericClass<int>();//实例化泛型类用<>括号携带类型参数int
intInstance.SetValue(42);
Console.WriteLine(intInstance.GetValue());  // 输出: 42var stringInstance = new GenericClass<string>();//实例化泛型类用<>括号携带类型参数string
stringInstance.SetValue("Hello, World!");
Console.WriteLine(stringInstance.GetValue());  // 输出: Hello, World!// 定义泛型类
public class GenericClass<T>
{private T _value;public void SetValue(T value){_value = value;}public T GetValue(){return _value;}
}

泛型方法

  • 泛型方法是在方法名之前加上尖括号 <T>,其中 T 是类型参数,表示方法可以操作的某种类型。
  • 注意:并不是只有泛型类中可以定义泛型方法,普通类中也可以定义泛型方法。
// 使用泛型方法
int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"x: {x}, y: {y}");  // 输出: x: 2, y: 1string str1 = "first", str2 = "second";
Swap(ref str1, ref str2);
Console.WriteLine($"str1: {str1}, str2: {str2}");  // 输出: str1: second, str2: first// 定义一个泛型方法
static void Swap<T>(ref T a, ref T b)
{T temp = a;a = b;b = temp;
}

泛型接口

  • 定义泛型接口的语法与泛型类类似。接口名后面的尖括号 <T> 表示这是一个泛型接口,T 是一个占位符,表示数据的类型。
  • 下面的例子定义了一个通用的存储接口 IRepository<T>,可以处理任意类型的 T,如 intstring,或者自定义的对象。
// 定义泛型接口
public interface IRepository<T>
{void Add(T item);T Get(int id);void Delete(T item);
}// 实现泛型接口,指定为 int 类型
public class IntRepository : IRepository<int>
{private List<int> _items = new List<int>();public void Add(int item){_items.Add(item);}public int Get(int id){return _items[id];}public void Delete(int item){_items.Remove(item);}
}// 实现泛型接口,指定为 string 类型
public class StringRepository : IRepository<string>
{private List<string> _items = new List<string>();public void Add(string item){_items.Add(item);}public string Get(int id){return _items[id];}public void Delete(string item){_items.Remove(item);}
}// 使用泛型接口
class Program
{static void Main(string[] args){IRepository<int> intRepo = new IntRepository();intRepo.Add(10);Console.WriteLine(intRepo.Get(0));  // 输出: 10IRepository<string> stringRepo = new StringRepository();stringRepo.Add("Hello");Console.WriteLine(stringRepo.Get(0));  // 输出: Hello}
}

泛型委托

  • 定义泛型委托的语法与泛型类和泛型接口类似。委托名后面的尖括号 <T> 表示这是一个泛型委托,T 是一个占位符,表示数据的类型。
  • 下面的例子定义了一个通用的委托 MyDelegate<T>,可以处理任意类型的 T,如 intstring,或者自定义的对象。
class Program
{// 定义泛型委托public delegate void MyDelegate<T>(T item);static void Main(string[] args){// 使用泛型委托,传递类型参数intMyDelegate<int> intDelegate = (int item) =>{Console.WriteLine($"Int: {item}");};// 调用委托intDelegate(42);  // 输出: Int: 42}
}

  • 泛型委托不仅可以用于单个类型,还可以处理多个类型参数,
  • 例如处理多个输入参数或返回值的委托,以下是定义具有两个泛型参数的委托的示例,MyFunc<T1, T2, TResult> 是一个带有两个输入类型和一个返回类型的泛型委托。
  • 通过这种方式,我们可以处理更加复杂的场景,例如根据多个输入生成一个返回值。
// 使用MyFunc<T1, T2, TResult>
MyFunc<int, string, string> func = Combine;//这里T1是int,T2是string,TResult是string
string result = func(100, "Hello");
Console.WriteLine(result);  // 输出: 100 - Hello// 定义 Combine 方法
string Combine(int number, string text)
{return $"{number} - {text}";
}
// 定义一个带有两个泛型参数的委托
public delegate TResult MyFunc<T1, T2, TResult>(T1 item1, T2 item2);

泛型委托的使用场景

// 1. Action<T> 示例:不返回值的泛型委托,接受一个参数
Action<string> printMessage = (string message) =>
{Console.WriteLine($"Message: {message}");
};
printMessage("Hello, World!");  // 输出: Message: Hello, World!// 2. Func<T, TResult> 示例:返回值的泛型委托,接受多个参数并返回一个结果
Func<int, int, int> addNumbers = (int x, int y) =>
{return x + y;
};
int sum = addNumbers(10, 20);
Console.WriteLine($"Sum: {sum}");  // 输出: Sum: 30// 3. Predicate<T> 示例:返回布尔值的泛型委托,用于判断条件
Predicate<int> isEven = (int number) =>
{return number % 2 == 0;
};
bool result = isEven(10);
Console.WriteLine($"Is 10 even? {result}");  // 输出: Is 10 even? True// 结合 Predicate 和 List.FindAll 方法,筛选出偶数
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evenNumbers = numbers.FindAll(isEven);
​
Console.WriteLine("Even numbers:");
foreach (int number in evenNumbers)
{Console.WriteLine(number);  // 输出: 2, 4, 6, 8, 10
}

泛型约束

  • 定义泛型约束的语法与定义泛型基本相同,只需要在类型参数<T>后面通过 where 关键字来指定约束条件。
  • 常见的约束类型包括继承约束、接口约束、构造函数约束等。
  • 下面的例子定义了一个使用泛型约束的类 Repository<T>,其中 T 必须继承自 Entity,并且必须有一个无参构造函数,如果不满足这两个条件编译器就会报错 。
// 定义基类 Entity
public class Entity
{public int Id { get; set; }
}// 定义泛型类 Repository<T>,带有两个约束:T 必须继承自 Entity 并且有无参构造函数
public class Repository<T> where T : Entity, new()
{public T Create(){return new T();  // 这里要求 T 必须有无参构造函数}
}// 定义符合约束的类 ValidEntity,有无参构造函数
public class ValidEntity : Entity
{public ValidEntity(){Id = 1;}
}// 定义不符合约束的类 InvalidEntity,没有无参构造函数
public class InvalidEntity : Entity
{public InvalidEntity(int id){Id = id;}
}class Program
{static void Main(string[] args){// 使用符合约束的类 ValidEntityRepository<ValidEntity> validRepo = new Repository<ValidEntity>();ValidEntity validEntity = validRepo.Create();Console.WriteLine($"ValidEntity ID: {validEntity.Id}");  // 输出: ValidEntity ID: 1// 使用不符合约束的类 InvalidEntity 会导致编译错误// Repository<InvalidEntity> invalidRepo = new Repository<InvalidEntity>();// InvalidEntity invalidEntity = invalidRepo.Create();  // 无法编译,InvalidEntity 缺少无参构造函数// 编译错误提示:// "InvalidEntity" 没有无参构造函数,无法满足 new() 约束。}
}

泛型约束的使用场景

泛型约束可以确保类型的安全性,减少不必要的类型转换错误。以下是一些使用泛型约束的场景:下面有具体的实例代码

约束类型描述使用场景
where T : class限制类型参数为引用类型。适用于泛型类必须处理引用类型的场景,如对象操作。
where T : struct限制类型参数为值类型。适用于泛型类处理数值类型时,确保性能和类型安全。
where T : new()限制类型参数必须具有无参构造函数。适用于需要实例化泛型类型的场景。
where T : BaseClass限制类型参数必须是某个类的子类。适用于泛型类型继承某个基类,复用基类功能的场景。
where T : InterfaceName限制类型参数必须实现某个接口。适用于泛型类型必须实现接口,并使用接口方法的场景。

类约束

限制类型参数必须是某个类或某个类的子类。适用于当你希望类型参数继承某个类并复用该类的功能时。

// 定义一个基类
public class Animal
{public string Name { get; set; }public void Speak(){Console.WriteLine($"{Name} makes a sound.");}
}// 定义一个带有类约束的泛型类,T 必须继承自 Animal
public class AnimalHouse<T> where T : Animal
{public void Welcome(T animal){animal.Speak();  // 可以调用基类 Animal 的方法}
}class Program
{static void Main(){AnimalHouse<Animal> house = new AnimalHouse<Animal>();Animal dog = new Animal { Name = "Dog" };house.Welcome(dog);  // 输出: Dog makes a sound.}
}

接口约束

限制类型参数必须实现某个接口。适用于当你希望类型参数实现某些方法或属性时。

// 定义一个接口
public interface IRun
{void Run();
}// 实现接口的类
public class Person : IRun
{public void Run(){Console.WriteLine("Person is running");}
}// 定义一个带有接口约束的泛型类,T 必须实现 IRun 接口
public class Race<T> where T : IRun
{public void Start(T runner){runner.Run();  // 可以调用 IRun 接口中的方法}
}class Program
{static void Main(){Race<Person> race = new Race<Person>();Person person = new Person();race.Start(person);  // 输出: Person is running}
}

构造函数约束

限制类型参数必须有一个无参构造函数。这常用于当你需要在泛型类或方法中创建类型实例时。

// 定义一个带有构造函数约束的泛型类,T 必须有无参构造函数
public class Factory<T> where T : new()
{public T CreateInstance(){return new T();  // 可以安全地创建 T 类型的实例}
}// 使用该类
class Program
{static void Main(){Factory<Person> factory = new Factory<Person>();Person person = factory.CreateInstance();Console.WriteLine("Instance of Person created.");}
}// Person 类有一个无参构造函数
public class Person
{public string Name { get; set; }
}

值类型约束

限制类型参数必须是值类型。这在处理数值或结构体时非常有用,因为值类型有更好的性能表现。

// 定义一个带有值类型约束的泛型类,T 必须是值类型
public class MathOperations<T> where T : struct
{public T Add(T a, T b){dynamic x = a;  // 使用动态类型进行数学操作dynamic y = b;return x + y;}
}// 使用该类
class Program
{static void Main(){MathOperations<int> mathOps = new MathOperations<int>();int result = mathOps.Add(10, 20);Console.WriteLine($"Result: {result}");  // 输出: Result: 30}
}

引用类型约束

限制类型参数必须是引用类型。这在你希望处理对象而非值类型时非常有用。

// 定义一个带有引用类型约束的泛型类,T 必须是引用类型
public class Repository<T> where T : class
{private List<T> _items = new List<T>();public void AddItem(T item){_items.Add(item);}public T GetItem(int index){return _items[index];}
}// 使用该类
class Program
{static void Main(){Repository<Person> repo = new Repository<Person>();repo.AddItem(new Person { Name = "John" });Person person = repo.GetItem(0);Console.WriteLine($"Retrieved Person: {person.Name}");  // 输出: Retrieved Person: John}
}public class Person
{public string Name { get; set; }
}

http://www.dtcms.com/a/550135.html

相关文章:

  • 网站建设研究方法建筑图纸字母代表大全图解
  • PocketBase轻量级后端解决方案
  • 【JavaWeb|day19 Web后端进阶 SpringAOP、SpringBoot原理、自定义Starter、Maven高级】
  • 织梦可以放两个网站网站内容架构
  • ENSP Pro Lab笔记:配置STP/RSTP/MSTP(4)
  • 关于公司网站建设阿里巴巴logo高清图
  • Appium使用指南与自动化测试案例详解
  • 做网站的实践报告四川网站建设seo优化
  • 农村智慧养老:探索新时代养老新路径
  • vim上手
  • 在线课程软件网站建设费用济南网站建设的费用
  • 做全景网站live writer wordpress
  • 网站后台使用说明试用网站建设
  • 工作汇报和技术分享PPT如何高效制作?我的实用经验
  • Nginx基础入门-web模块
  • 从零开始的云原生之旅(七):ConfigMap 和 Secret 配置管理
  • 潍坊企业网站设计怎么注册一个自己的品牌
  • 展示网站欣赏wordpress 官网
  • C++:const 的空间,常量也能占内存?
  • 学习FreeRTOS(互斥量)
  • 网站如何进行优化设计高端网站官网
  • 江苏五星建设网站长沙网页设计培训找沙大计教育预约网址
  • 蓝牙钥匙 第18次 蓝牙技术在物联网中的定位:与NFC、UWB和蜂窝网络的对比分析与协同发展
  • 办公室无缝访问海外AWS:中国企业全球化数据协作的组网之道
  • 【Rust】路由匹配与参数提取:从 match 语句到 axum 的类型魔法
  • 滕州做网站哪家好高效完成网站建设的步骤
  • 鸿蒙NDK开发实战指南:从ArkTS到C/C++的高性能桥梁
  • 烟台规划网站做个爬架网站如何做
  • rust实战
  • 制作一个网站需要多少钱什么软件做网站