• 友链

  • 首页

  • 文章归档
h u a n b l o g
h u a n b l o g

欢

HI,Friend

04月
13
C#

泛型

发表于 2022-04-13 • 字数统计 7453 • 被 1,394 人看爆

概念

通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用

泛型是类型的模板

泛型是类的模板.png

C#中提供5中泛型:类、结构、接口、委托和方法
前四种是类型,而方法是成员

泛型与用户自定义类型.png

例

声明一个MyIntStack类,该类实现一个int类型的栈,进行入栈和出栈操作

class MyIntStack
{

	int StackPointer = 0;
	int[] StackArray;

	public void Push(int x) {
		....
	}

	public int Pop() {
		...
	}
}

如果这时需要使用float类型,常用的就是将代码重新复制一份,修改类型,这样就产生很多重复代码,浪费额外空间,而采用泛型就可避免

采用泛型

class MyIntStack<T>
{

	int StackPointer = 0;
	T[] StackArray;

	public void Push(T x) {
		....
	}

	public T Pop() {
		...
	}
}

这样类型就不限制了

泛型类

声明泛型类

规则

  • 在类名之后放置一组尖括号。
  • 在尖括号中用逗号分隔的占位符字符串来表示希望提供的类型。这叫做类型参数。
  • 在泛型类声明的主体中使用类型参数来表示应该替代的类型。

语法

//T1、T2是类型占位符
class SomeClass<T1, T2>
{
	public T1 SomeeVar = new T1();
	public T2 OtherVar = new T2();
}

构造类型

用来告诉编译器要使用哪些真实类型来替代占位符

规则

列出类名并在尖括号中提供真实类型来替代类型参数

语法

SomeClass<short, int>

//则会将T1,T2按照参数位置进行替换
class SomeClass<short, int>
{
	public short SomeeVar = new short();
	public int OtherVar = new int();
}

实例

进行栈查找

class MyStack<T>
{
    T[] StackArray;
    int StackPointer = 0;       //当前栈元素个数
    const int MaxStack = 10;

    public MyStack()
    {
        StackArray = new T[MaxStack];
    }

    //判栈满
    bool IsStackFull
    {
        get
        {
            return StackPointer >= MaxStack;
        }
    }

    //判栈空
    bool IsStackEmpty
    {
        get
        {
            return StackPointer <= 0;
        }
    }

    public void Push(T x)
    {
        if (!IsStackFull)
            StackArray[StackPointer++] = x;
    }

    public T Pop()
    {
        return (!IsStackEmpty) ? StackArray[--StackPointer] : StackArray[0];
    }

    public void Print()
    {
        for(int i = StackPointer - 1; i >= 0; i--)
        {
            Console.WriteLine($"Value:{StackArray[i]}");
        }
    }

}

class Program
{
    static void Main(string[] args)
    {
        MyStack<int> StackInt = new MyStack<int>();
        MyStack<string> StackString = new MyStack<string>();

        StackInt.Push(3);
        StackInt.Push(5);
        StackInt.Push(7);
        StackInt.Push(9);
        StackInt.Print();

        StackString.Push("输入字符");
        StackString.Push("你好");
        StackString.Print();

        Console.ReadKey();
    }
}

类型参数约束

由于泛型是占位符,并不确定类型,所以无法进行一些运算符操作.如:+、-等

例

class Simple<T>
{
	static public bool LessThan(T i1, T i2) {
		return i1< i2;		//错误
	}
}

where子句

约束使用where字句列出

用法

  • 每一个有约束的类型参数有自己的where子句。
  • 如果形参有多个约束,它们在where子句中使用逗号分隔。

语法

//TypeParam类型参数		constraint约束列表
where TypeParam : constraint, constraint...		

要点

  • 它们在类型参数列表的关闭尖括号之后列出。
  • 它们不使用逗号或其他符号分隔。
  • 它们可以以任何次序列出。
  • where是上下文关键字,所以可以在其他上下文中使用。

例

例如,如下泛型类有3个类型参数。T1是未绑定的,对于T2,只有Customer类型或从Customer继承的类型的类才能用作类型实参,而对于T3,只有实现IComparable接口的类才能用于类型实参。

class MyClass<T1, T2, T3> 
		where T2: Custome		//T2的约束
		where T3: IComparable	//T3的约束
{
	
} 

约束类型和次序

类型

约束类型描述
类名只有这个类型的类或从它继承的类才能用作类型实参
class任何引用类型,包括类、数组、委托和接口都可以用作类型实参
struct任何值类型都可以用作类型实参
接口名只有这个接口或实现这个接口的类型才能用作类型实参
new()任何带有无参公共构造函数的类型都可以用作类型实参。这叫做构造函数约束

次序

  • 最多只能有一个主约束,如果有则必须放在第一位。
  • 可以有任意多的接口名约束。
  • 如果存在构造函数约束,则必须放在最后。

类型约束次序.png

实例

class SortedList<S> 
	where S : IComparable<S> 
{
	....
}

class LinkedList<M, N> 
	where M : IComparable<M> 
	where N : ICloneable
{
	....
}

class MyDictionary<KeyType, ValueType> 
	where KeyType : IEnumerable,
	new()
{
	....
}

new约束

指定泛型类声明中的类型实参必须有公共的无参数构造函数

规则

  • 若要使用new约束,则该类型不能为抽象类型
  • 当与其他约束一起使用时,new()约束必须最后指定:

例

class ItemFactory<T> where T : new()
{
    public T GetNewItem()
    {
        return new T();
    }
}


public class ItemFactory2<T>
    where T : IComparable, new()
{  }

泛型方法

声明泛型方法

  • 泛型方法有两个参数列表。
    • 封闭在圆括号内的方法参数列表。
    • 封闭在尖括号内的类型参数列表。
  • 要声明泛型方法,需要:
    • 在方法名称之后和方法参数列表之前放置类型参数列表;
    • 在方法参数列表后放置可选的约束子句。

例

//<S, T>类型参数列表	(S p, T t)方法参数列表		
public void PrintData<S, T>(S p, T t) where S:Person
{
	...
}

调用


MyMethod<short, int>();

扩展方法和泛型类

扩展方法知识点

规则

  • 必须声明为static;
  • 必须是静态类的成员;
  • 第一个参数类型中必须有关键字this,后面是扩展的泛型类的名字。

实例

class Holder<T>
{
    T[] vals = new T[3];
    public Holder(T v0, T v1, T v2)
    {
        vals[0] = v0;
        vals[1] = v1;
        vals[2] = v2;
    }

    public T[] GetValues()
    {
        return vals;
    }
}

static class ExtendHolder
{
    public static void Print<T>(this Holder<T> h)
    {
        T[] vals = h.GetValues();
        Console.WriteLine($"vals0:{vals[0]},vals1:{vals[1]},vals2:{vals[2]}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var intHolder = new Holder<int>(3, 5, 7);
        var stringHolder = new Holder<string>("a1", "a2", "a3");
        intHolder.Print();
        stringHolder.Print();

        Console.ReadKey();
    }
}

结果

vals0:3,vals1:5,vals2:7
vals0:a1,vals1:a2,vals2:a3

泛型结构

规则与泛型类一样
struct PieceOfData<T>
{
    private T _data;
    public PieceOfData(T  value)
    {
        _data = value;
    }

    public T Data
    {
        get { return _data; }
        set { _data = value; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var intData = new PieceOfData<int>(10);
        var stringData = new PieceOfData<string>("嗨,你好");
        Console.WriteLine($"intData:{intData.Data}");
        Console.WriteLine($"stringData:{stringData.Data}");

        Console.ReadKey();
    }
}

结果

intData:10
stringData:嗨,你好

泛型委托

规则

  • 要声明泛型委托,在委托名称后、委托参数列表之前的尖括号中放置类型参数列表。
//R返回类型		<T, R> 类型参数		(T value) 委托形参
delegate R MyDelegate<T, R>(T value);
  • 两个参数列表:委托形参列表 和 类型参数列表
  • 类型参数范围
    • 返回值
    • 形参列表
    • 约束子句

实例1

delegate void MyDelegater<T>(T value);  //泛型委托

class Simple
{
    static public void PrintString(string s)        //方法匹配委托
    {
        Console.WriteLine(s);

    }

    static public void PrintUpperString(string s)
    {
        Console.WriteLine($"s.ToUpper:{s.ToUpper()}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var myDel = new MyDelegater<string>(Simple.PrintString);
        myDel += Simple.PrintUpperString;
        myDel("你好");
        Console.ReadKey();
    }
}

结果

你好
s.ToUpper:你好

实例2

//TR返回类型
delegate TR Func<T1, T2, TR>(T1 p1, T2 p2);  //泛型委托

class Simple
{
    static public string PrintString(int p1, int p2)        //方法匹配委托
    {
        int total = p1 + p2;
        return total.ToString();

    }
}

class Program
{
    static void Main(string[] args)
    {
        var myDel = new Func<int, int, string>(Simple.PrintString);
        
        Console.WriteLine($"Total:{myDel(15, 13)}");
        Console.ReadKey();
    }
}

结果

Total:28

泛型接口

interface IMyIfc<T>
{
    T RturnIt(T inValue);
}

class Simple<S> : IMyIfc<S>
{
    public S RturnIt(S inValue)
    {
        return inValue;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var trivInt = new Simple<int>();
        var trivString = new Simple<string>();
        Console.WriteLine($"trivInt:{trivInt.RturnIt(5)}");
        Console.WriteLine($"trivString: {trivString.RturnIt("你好")}");

        Console.ReadKey();
    }
}

结果

trivInt:5
trivString: 你好

分享到:
枚举器
类型转换关键字
  • 文章目录
  • 站点概览
欢

网红 欢

你能抓到我么?

Email RSS
看爆 Top5
  • mac系统版本与Xcode版本有冲突 4,089次看爆
  • JAVA_HOME环境配置问题 3,739次看爆
  • AssetBundle使用 3,507次看爆
  • VSCode配置C++开发环境 3,262次看爆
  • Lua反射 3,138次看爆

Copyright © 2025 欢 粤ICP备2020105803号-1

由 Halo 强力驱动 · Theme by Sagiri · 站点地图