概念
让chatGpt
来为我们讲解。
在C#
中,泛型是一种允许开发人员编写可重用代码,可以处理多种数据类型的特性。
使用泛型,可以创建类、方法、接口和委托这种不属于任何特定数据的类型,但可以处理满足某些约束条件的任何数据类型。
主要优点:
-
让代码更加通用。
-
在编译时提供类型安全性,也可以避免装箱和拆箱的操作,从而提高性能
-
我们没必要用转换了,代码更简洁了
以下是C#中泛型类的示例:
public class MyGenericClass<T> {
private T myVariable;
public MyGenericClass(T value) {
myVariable = value;
}
public T GetValue() { return myVariable; }
}
在此示例中,使用类型参数T
定义了MyGenericClass
类。构造函数接受类型为T
的值并将其存储在私有变量中,GetValue
方法返回存储的值。
要使用特定的数据类型使用此类,可以在创建类的实例时指定数据类型,如下所示:
MyGenericClass<int> myIntClass = new MyGenericClass<int>(42);
int myIntValue = myIntClass.GetValue();
在此示例中,使用类型参数为int
创建了MyGenericClass
类的myIntClass
实例,GetValue
方法返回一个int
值。
创建方式
接下来我们分别讲讲有那些写法,可供我们需要的时候参考。
单个参数类型T
使用泛型类型T
来创建的数组可以随意指定,这样做的好处是可以重复使用CreateArray
方法来创建任何数据类型的数组,而不需要为每个数据类型编写不同的代码。
public class TestingGenerics1 : MonoBehaviour
{
private void Start()
{
int[] intArray = CreateArray<int>(5, 6);
Debug.Log(intArray.Length + " " + intArray[0] + " " + intArray[1]);
Debug.Log(intArray.GetType());//int32
Debug.Log(CreateArray<string>("a","b").GetType());//string
}
//为任何数据类型创建数组,接受由泛型类型T定义的两个参数
private T[] CreateArray<T>(T first,T second)
{
return new T[] { first, second };
}
}
多个参数类型T
public class TestingGenerics1 : MonoBehaviour
{
private void Start()
{
CreateMultiArray<int,string>(5, "6");
}
private void CreateMultiArray<T1,T2>(T1 first,T2 second)
{
Debug.Log(first.GetType());
Debug.Log(second.GetType());
}
}
泛型委托和预定义委托的类型
public class TestingGenerics1 : MonoBehaviour
{
//两个参数,不返回值
private delegate void MyActionDelegate<T1, T2>(T1 t1, T2 t2);
private Action<int,string> action;
//一个参数类型T1,返回类型为TResult的值
private delegate TResult MyFuncDelegate<T1, TResult>(T1 t1);
private Func<int,bool> func;
}
泛型和接口
目标:
- 创建一个可重复使用的
MyClass
类,它可以使用任何实现了IEnemy
接口的类型作为其类型参数T
。
好处:
- 提高代码的可重用性和可维护性。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestingGenerics1 : MonoBehaviour
{
private void Start()
{
//创建了两个MyClass类的实例,分别传递了一个EnemyMinion和一个EnemyArcher类型的对象。
MyClass<EnemyMinion> myClass = new MyClass<EnemyMinion>(new EnemyMinion());
MyClass<EnemyArcher> myClassArcher = new MyClass<EnemyArcher>(new EnemyArcher());
}
}
//MyClass类是一个泛型类,其类型参数T必须是实现了IEnemy接口的类型。
public class MyClass<T> where T : IEnemy
{
//包含一个公共字段value,它存储传递给构造函数的值
public T value;
//并在构造函数中调用了这个值的Damage()方法。
public MyClass(T value)
{
value.Damage();
}
private T[] PersonalAction(T first, T second){...}
}
//IEnemy接口定义了一个方法Damage(),表示敌人受到攻击时所需执行的动作。
public interface IEnemy
{
void Damage();
}
//EnemyMinion和EnemyArcher类都实现了IEnemy接口,并分别定义了它们自己的Damage()方法。
public class EnemyMinion : IEnemy
{
public void Damage()
{
Debug.Log("EnemyMinion.Damage()");
}
}
public class EnemyArcher : IEnemy
{
public void Damage()
{
Debug.Log("EnemyArcher.Damage()");
}
}
增强灵活性和重用性
public class TestingGenerics1 : MonoBehaviour
{
private void Start()
{
MyClass<EnemyMinion> myClass = new MyClass<EnemyMinion>(new EnemyMinion());
MyClass<EnemyArcher> myClassArcher = new MyClass<EnemyArcher>(new EnemyArcher());
}
}
/*不同点1:添加额外约束*/
//要求类型参数T必须是一个类(即引用类型),必须实现IEnemy<T>接口,且必须有一个无参数构造函数。
public class MyClass<T> where T : class,IEnemy<int>,new()
{
public T value;
public MyClass(T value)
{
value.Damage(0);
}
private T[] CreateArray(T first, T second)
{
return new T[] { first, second };
}
}
public interface IEnemy<T>
{
/*不同点2:Damage方法的参数类型变为了T类型,即泛型类型*/
//这意味着,敌人类可以使用不同的数据类型作为受到攻击时所需执行的参数类型。
void Damage(T t);
}
public class EnemyMinion : IEnemy<int>
{
//与IEnemy<T>接口的定义相匹配
public void Damage(int i)
{
Debug.Log("EnemyMinion.Damage()"+i);
}
}
public class EnemyArcher : IEnemy<int>
{
public void Damage(int i)
{
Debug.Log("EnemyArcher.Damage()"+i);
}
}
在游戏中的使用例子
场景:
你现在要创建一个角色,而我们知道创建一个角色前,我们是可以选定角色的职业的,假设我们要弄一个简易的游戏角色生成器,而你要创建两种不同类型的角色:saber、assassin
,我们会怎么做?
这次我就不写没学过泛型概念的一般人是怎么想的了
1.创建一个基类Role
,表示共同属性
// 定义一个角色类,包含姓名和职业两个属性
public class Role
{
public string Name { get; set; }
public string Job { get; set; }
public int HP { get; set; }
public int MP { get; set; }
public virtual void Attack()
{
Debug.Log(this.Name + " attacks with a normal attack.");
}
}
2.为了表示职业特殊,角色的武器不同
// 定义 Saber 类和 Assassin 类,继承自角色类
public class Saber : Role {
//重载
public override void Attack()
{
Debug.Log(this.Name +" is "+ this.Job +" attacks with a sword. " +
"\n her HP:"+this.HP+"MP:"+this.MP);
}
}
public class Assassin : Role {
public override void Attack()
{
Debug.Log(this.Name + " is " + this.Job + " attacks with a dagger. " +
"\n his HP:" + this.HP + "MP:" + this.MP);
}
}
3.创建一个简易函数,表示角色生成器
// 定义一个角色生成器类,包含一个泛型方法,用于生成指定职业的角色
public class RoleGenerator
{
public T GenerateRole<T>(string name,int HP,int MP) where T : Role, new()
{
T role = new T();
role.Name = name;
role.HP = HP;
role.MP = MP;
if (typeof(T) == typeof(Saber))
{
role.Job = "Saber";
}
else if (typeof(T) == typeof(Assassin))
{
role.Job = "Assassin";
}
return role;
}
}
4. 测试
public class TestingGenerics1 : MonoBehaviour
{
private void Start()
{
// 创建角色生成器实例
RoleGenerator generator = new RoleGenerator();
// 生成一个名为 "Artoria" 的 Saber 类型的角色
Saber saber = generator.GenerateRole<Saber>("呆毛",100,20);
saber.Attack();
// 生成一个名为 "Jack" 的 Assassin 类型的角色
Assassin assassin = generator.GenerateRole<Assassin>("山中老人",200,10);
assassin.Attack();
}
}
总结
C#中的泛型,是一种强大的功能,可以创建一个特定的类,可以与任何类型一起使用。
本文解释了如何编写一个类或函数,而不定义任何特定类型,并在使用类或函数时传递类型。
此外还涵盖了如何在委托中使用泛型,向泛型添加约束以及在接口中使用泛型。
提供了使用泛型创建网格系统、创建返回包含任何类型元素的数组的函数以及创建可以使用泛型类型的类的示例。
题外话
本来想让chatGPT顺便提供一些在unity游戏开发的使用场景,但是它回答的实在是听君一席话,如听一席话
,我还是不发了,免得有像我一样的小白看不懂。