C#泛型和非泛型集合

泛型和非泛型集合

一、泛型

泛型是指不特定于某种数据类型。
通过在类型名称后的尖括号中指定类型参数来声明泛型,例如TypeName<T>,这里 T 是类型参数。

额外补充-属性

public int IntValue0 { get; set; } // 这句相当于以下段落

实例一(1):public int IntValue0 { get; set; }

 // 声明private整型IntValue1
private int IntValue1;

 //方法赋予IntValue1数值(private修饰导致IntValue1访问仅在对象本身内部使用)
public void SetIntValue1(int value) { IntValue1 = value; }  

 // 使用(return IntValue1)获取IntValue1数值
public int GetIntValue1() { return IntValue1; } 

实例一(2):

public void F1()
{
    int b = IntValue0;      // 使用属性get
    IntValue0 = 10;         // 使用属性set

    int a = GetIntValue1(); // 使用方法get
    SetIntValue1(10);       // 使用方法set
}

/*
public int IntValue { get; set; }
这种写法是自动属性,自动生成他的get set功能。
是为了以后设计变更的时候 更容易扩展和修改
*/

// 常规写法
private float _FloatValue1;
public float FloatValue1
{
    get // 这里相当于有个返回值
    {
        return _FloatValue1;
    }
    set // 这里相当于有个参数
    {
        _FloatValue1 = value;
    }
}

// 属性的 get set 可以单独设置权限,默认public
// 但是只允许设置一边
public int IntValue3 { get; private set; }

实例一(3):使用属性示例

// 常见用法
private float _Hp;                // 声明私有定义 _Hp
public float Hp                   // 声明公共浮点数 Hp
{
    get                           // 获取_Hp数值为Hp值
    {
        return _Hp;
    }
    set                           // 赋值  
    {
        _Hp = value;              // 当Hp接受赋值时调用set(value值为外部赋值时的数值)
        if (_Hp < 0)              // 如果_Hp小于0
        {
            _Hp = 0;              // 使_Hp等于0
        }
    }
}
public void Damage(int damage)    // 定义一个方法Damage
{
    Hp = Hp - damage;             // Hp赋值为Hp减去传入的damege数值
}

1.泛型类

实例一(3):泛类型主要格式

// 单个类型泛型
class DataStore<T>
{
    public T Data { get; set; }
}

// 多个类型的泛型类
class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
}

2.泛型字段

泛型类可以包含泛型字段。但是无法初始化。

// 泛型字段
class DataStore<T>
{
    public T data;
}

// 泛型数组
class DataStore<T>
{
    public T[] data = new T[10];
}

3.泛型方法

实例一(4):泛型方法1

// 泛型方法使用

public class Sample :MonoBehaviour
{
    public void Print3<T>(T value)  //方法Print
    {
        Debug.Log($"这是一个{typeof(T).FullName}值,value = {value}");
    }

    // 泛型使用
    Print3<int>(10);        // 输出 "这是一个int值,10"
    Print3<bool>(false);    // 输出 "这是一个bool值,false"
}

实例一(5):泛型方法2

class DataStore<T>
{
    private T[] _data = new T[10];
    
    public void AddOrUpdate(int index, T item)
    {
        if(index >= 0 && index < 10)
            _data[index] = item;
    }

    public T GetData(int index)
    {
        if(index >= 0 && index < 10)
            return _data[index];
        else 
            return default(T);
    }
}
// 上面的 AddorUpdate() 和 GetData() 方法是泛型方法。

4.泛型约束

在声明泛型方法/泛型类的时候,可以给泛型加上一定的约束来满足我们特定的一些条件。

实例一(6):泛型约束

    // 格式如下
    // public void Print4<T>(T value) where T : new()

    public void Print4<T>(T value)
    where T : new()
        //where T : struct      // 只能是struct值类型
        //where T : class       // 只能是class类
        //where T : new()       // 必须有空构造函数
        //where T : BaseClass   // 基类约束
        //where T : IInterface  // 接口约束
        //where T : U           // 类型参数约束
    {
    }


    // where T : struct 值类型约束

    // T 只能是值类型 
    // 正确情况:
    MyBox<int> box1 = new MyBox<int>(); 
    MyBox<Vector3> box2 = new MyBox<Vector3>(); // Unity 常用结构体
    // 错误情况:
    //MyBox<string> box3 = new MyBox<string>(); // string 是引用类型


    // where T : class 引用类型约束

    // T 必须是引用类型(可以是null)
    // 正确情况:
    MyContainer<string> c1 = new MyContainer<string>();
    MyContainer<List<int>> c2 = new MyContainer<List<int>>();
    MyContainer<MyMonoBehaviour> c3 = new MyContainer<MyMonoBehaviour>();
    // 错误情况:
    //MyContainer<int> c4 = new MyContainer<int>(); // int 是值类型


    // where T : new() 无参构造函数约束

    // T 必须有一个公开且无参数的构造函数
    //如果同时有多个约束,new() 必须放在最后。
    //允许你在泛型类内部使用 new T() 创建实例。
    public class Factory<T> where T : class, new() 
                                          // new() 在最后
    {
        public T CreateInstance()
        {
            return new T(); // 只有加了 new() 约束才能这样写
        }
    }
    // 错误情况: new() 没有放在最后
    // public class BadFactory<T> where T : new(), class { } 


    // where T : BaseClass 基类约束

    // T 必须是指定的基类,或者派生自该基类
    public class Enemy 
    { 
        public virtual void Attack() 
        {                
        }
    }
    public class Boss : Enemy 
    { 
    }
    public class Army<T> where T : Enemy
    {
        public void StartAttack(T unit)
        {
            // 编译器知道 T 一定是 Enemy 或其子类,所以可以调用 Attack()
            unit.Attack(); 
        }
    }
    Army<Boss> bossArmy = new Army<Boss>();
    // 错误情况:
    Army<string> stringArmy = new Army<string>(); // 因为string 不继承自 Enemy


    // where T : IInterface 接口约束

    // T 必须实现指定的接口
    public interface IDamageable 
    {
        void TakeDamage(int amount); 
    }
    public interface IHealable 
    { 
        void Heal(int amount); 
    }
    // T 必须同时实现这两个接口
    public class BattleUnit<T> where T : IDamageable, IHealable
    {
        public void Fight(T unit)
        {
            unit.TakeDamage(10);
            unit.Heal(5);
        }
    }


    // where T : U 裸类型约束/类型参数约束

    // T 必须是 U,或者派生自 U。这里 U 是另一个类型参数
    // T 必须派生自 U (或者 T 就是 U)
    public class Pair<T, U> where T : U
    {
        public T Item1;
        public U Item2;
        public void Assign()
        {
            // 因为 T 是 U 的子类,所以 T 可以赋值给 U
            Item2 = Item1; 
        }
    }


    //组合约束

    //可以将上述约束组合使用,但必须遵守以下顺序规则:
    //基类(如果有,必须是第一个,且只能有一个)。
    //接口(可以有多个,任意顺序)。
    //struct 或 class(通常放在接口之后,建议放在中间)。
    //new()(必须是最后一个)。

    // T 必须继承自 MonoBehaviour (基类)
    // 并且实现 IComparable (接口)
    // 并且有无参构造函数 (new())
    public class MyComponent<T> where T : MonoBehaviour, IComparable, new()
    {
    }

二、泛型集合

1.列表

列表List <T>是强类型对象的集合,可以通过索引对其进行访问,并具有用于排序,搜索和修改列表的方法。

实例二:列表的主要结构与使用

// 列表
// 可以显式初始化
List<int> l = new() { 0, 1, 2, 3 };

List<int> list = new();

// 添加
list.Add(0);
list.Add(1);

// 修改
list[0] = 123;

// 删除下标
list.RemoveAt(0);

//删除所有元素
list.Clear(); 

//排序
list.Sort();

public int FSort(int x, int y)              //详细见下方//排序详解
{
    return -x.CompareTo(y);
}

// 遍历
foreach (int each in list)                  //foreach遍历
{
}
for (int i = 0; i < list.Count; i++)        //for循环从0到Count计数
{
}


//排序详解
public class sample : MonoBehaviour         //
public void F1()
{
    List<int> list = new() { 2, 3, 1 };
    list.Sort(); // 1, 2, 3
    list.Sort(FSort); // 3, 2, 1            //使用FSort方法
    list.Sort((x, y) => -x.CompareTo(y));   //等效上行内的方法
}

public int FSort(int x, int y)              //FSort方法
{
    return -x.CompareTo(y);
}

2.字典

字典的主要结构为Dictionary < TKey,TValue >它以不特定的顺序存储键值对

//字典可以显式初始化
Dictionary<string, int> d = new()
{
    {"a", 1},
    {"b", 2},
    {"c", 3},
};

Dictionary<string, int> dict = new();

// 添加"a"与对应的数据
dict.Add("a", 1);

// 修改"a"对应的数据
dict["a"] = 1;

// 获取"a"对应的数据
int a = dict["a"];

// 尝试获取未知键"a"的值
if (dict.TryGetValue("a", out int b)) 
{
    UnityEngine.Debug.Log(b);
}

// 查找是否含有未知键"a"
if (dict.ContainsKey("a")) 
{
}

//删除字典中的元素
dict.Remove("a"); // 删除 "a"

//删除所有元素
dict.Clear(); 

三、非泛型集合

1.动态数组

ArrayList 是一个非泛型的对象集合,其大小会动态增加。它与Array相同,只是它的大小是动态增加的

//创建
var arlist = new ArrayList();

//添加元素
arlist1.Add(1);
arlist1.Add("Bill");
arlist1.Add(" ");
arlist1.Add(true);
arlist1.Add(4.5);
arlist1.Add(null);

//使用 AddRange (ICollection c)方法在 ArrayList 中添加一个完整的 Array
var arlist2 = new ArrayList()
{
    1, "Bill", " ", true, 4.5, null
};
int[] arr = { 100, 200, 300, 400 };
arlist1.AddRange(arlist2); //在arraylist中添加arraylist 
arlist1.AddRange(arr); //在arraylist中添加数组 

//获取内容
//使用var关键字而不进行显式转换
var firstElement = arlist[0]; //返回1
var secondElement = arlist[1]; //返回"Bill"

//更新元素
arlist[0] = "Steve"; 
arlist[1] = 100;

// 遍历
foreach (int each in arlist)                //foreach遍历
{
}
for (int i = 0; i < arlist.Count; i++)      //for循环从0到Count计数
{
}

//在ArrayList中插入元素
arlist.Insert(1, "Second Item");

//删除内容
arList.Remove(null); //删除首次出现的null
arList.RemoveAt(4); //删除下标4处的元素
arList.RemoveRange(0, 2);//从第一个项目(下标为0)删除两个元素

C#泛型和非泛型集合
https://chooseqiu.com/posts/4cf0f5ba/
作者
Chooseqiu
许可协议