02011802 泛型02-泛型方法、泛型类的扩展方法、泛型结构、泛型委托、泛型接口
02011802 泛型02-泛型方法、泛型类的扩展方法、泛型结构、泛型委托、泛型接口
1. 泛型方法
- 方法是成员,不是类型。泛型方法可以在泛型和非泛型类以及结构和接口中声明。
1.1 声明泛型方法
- 泛型方法具有类型参数列表和可选的约束。
- 泛型方法有两个参数列表。
- 封闭在圆括号内的方法参数列表。
- 封闭在尖括号内的类型参数列表。
// 声明泛型方法语法格式
类型参数列表 约束子句
↓ ↓
public void PrintData<S, T>(S p, T t) where S : Person
↑
方法参数列表
说明,声明泛型方法,需要如下内容。
1. 在方法名称之后和方法参数列表之间放置类型参数列表。
2. 在方法参数列表后放置可选的约束子句。
1.2 调用泛型方法
类型实参
↓
MyMethod<short, int>();
1.3 推断类型
- 如果我们为方法传入参数,编译器有时可以从方法参数的类型中推断出用作泛型方法的类型参数的类型。
// @1 声明一个类型参数和方法形参类型相同的泛型方法
public void MyMethod<T>(T myVal) {...}
↑ ↑
两个都是T类型
说明:上述代码段表示声明了一个泛型方法,它接受一个与类型参数同类型的方法参数。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// @2 使用int类型的变量调用MyMethod,方法调用中的类型参数的信息就多余了,因为编译器可以从方法参数中得知它是int
int myInt = 5;
MyMethod<int>(myInt); // 调用泛型方法的普通形式
MyMethod(myInt); // 由于编译器可以从方法参数中推断出类型参数,我们可以省略类型参数和调用中的<>。
2. 泛型类的扩展方法
- 扩展方法可以和泛型类结合使用,这样我们可以将类中的静态方法关联到不同的泛型类上,还允许我们想调用类构造实例的实例方法一样来调用方法。
- 泛型类的扩展方法有如下注意事项。
- 必须声明为static。
- 必须是静态类的成员。
- 第一个参数类型中必须有this关键字,后面是扩展的泛型类的名字。
using System;
namespace Demo01
{
class Holder<T> // @1 声明一个泛型类
{
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) // @2 声明扩展方法,注意this。
{
T[] vals = h.GetValues();
Console.WriteLine($"{vals[0]},\t{vals[1]},\t{vals[2]}");
}
}
class Program
{
static void Main()
{
var intHolder = new Holder<int>(3, 5, 7);
var stringHolder = new Holder<string> ( "a1", "a2", "a3" );
intHolder.Print();
stringHolder.Print();
Console.ReadLine();
}
}
}
控制台输出:
3, 5, 7
a1, a2, a3
说明:上述代码中声明了一个Print的扩展方法,扩展了Holder<T>的泛型类。
3. 泛型结构
- 与泛型类相似,泛型结构可以有类型参数和约束。泛型结构的规则和条件与泛型类是一样的。
using System;
namespace Demo01
{
struct PieceOfData<T> // 泛型结构
{
public PieceOfData(T value)
{
Data = value;
}
public T Data { get; set; }
}
class Program
{
static void Main()
{
var intData = new PieceOfData<int>(10);
var stringData = new PieceOfData<string>("Qinway");
Console.WriteLine($"intData = {intData.Data}");
Console.WriteLine($"intData = {stringData.Data}");
Console.ReadLine();
}
}
}
控制台输出
intData = 10
intData = Qinway
4. 泛型委托
4.1 泛型委托基础
- 泛型委托和非泛型委托非常类似,泛型委托的参数决定了能接受什么样的方法。
// 声明泛型委托的语法
类型参数
↓
delegate R MyDelegate<T, R>(T value);
↑ ↑
返回类型 委托形参
说明:
1. 泛型委托有两个参数列表,委托参数列表和类型参数列表。
2. 类型参数列表包括:
2.1 返回类型
2.2 形参列表
2.3 约束子句
4.2 泛型委托的基础示例
using System;
namespace Demo01
{
delegate void MyDelegate<T>(T value); // @1 声明泛型委托
class Simple
{
static public void PrintString(string s) // @2 方法匹配委托
{
Console.WriteLine(s);
}
static public void PrintUpperString(string s) // @3 方法匹配委托
{
Console.WriteLine($"{s.ToUpper()}");
}
}
class Program
{
static void Main()
{
var myDel = new MyDelegate<string>(Simple.PrintString); // @4 创建委托的实例
myDel += Simple.PrintUpperString; // @5 给委托添加方法
myDel("Qinway"); // @6 调用委托
Console.ReadLine();
}
}
}
控制台输出:
Qinway
QINWAY
4.3 泛型委托进阶示例
using System;
namespace Demo01
{
public delegate TR Func<T1, T2, TR>(T1 p1, T2 P2); // @1 声明泛型委托
class Simple
{
static public string PrintString(int p1, int p2) // @2 方法匹配委托
{
int total = p1 + p2;
return total.ToString();
}
}
class Program
{
static void Main()
{
var myDel = new Func<int, int, string>(Simple.PrintString); // @3 创建委托实例
Console.WriteLine($"Total:{myDel(15, 13)}"); // @4 调用委托
Console.ReadLine();
}
}
}
控制台输出
Total:28
5. 泛型接口
- 泛型接口允许我们编写形参和接口成员返回类型是泛型类型参数的接口,泛型类型的接口和非泛型接口的声明差不多,但是需要在接口名称之后的尖括号中放置类型参数。
5.1 泛型接口基础用法
using System;
namespace Demo01
{
interface IMyIfc<T> // @1 声明泛型接口
{
T ReturnIt(T inValue);
}
class Simple<S> : IMyIfc<S> // @2 泛型类
{
public S ReturnIt(S inValue) // @3 实现泛型接口
{
return inValue;
}
}
class Program
{
static void Main()
{
var trivInt = new Simple<int>();
var trivString = new Simple<string>();
Console.WriteLine($"{trivInt.ReturnIt(10)}");
Console.WriteLine($"{trivString.ReturnIt("Qinway")}");
Console.ReadLine();
}
}
}
控制台输出:
10
Qinway
说明:在@2处,S是类型参数。
5.2 泛型接口进阶用法
- 与其它泛型类似,用不同类型参数实例化的泛型接口的实例是不同的接口。
using System;
namespace Demo01
{
interface IMyIfc<T> // @1 声明泛型接口
{
T ReturnIt(T inValue);
}
class Simple : IMyIfc<int>, IMyIfc<string>// @2 源于同一泛型接口的两个不同接口,这里Simple是非泛型类。
{
public int ReturnIt(int inValue) // @3.1 实现int类型接口
{
return inValue;
}
public string ReturnIt(string inValue) // @3.2 实现string类型接口
{
return inValue;
}
}
class Program
{
static void Main()
{
var triv = new Simple();
Console.WriteLine($"{triv.ReturnIt(10)}");
Console.WriteLine($"{triv.ReturnIt("Qinway")}");
Console.ReadLine();
}
}
}
控制台输出:
10
Qinway
注意,在@2处,Simple是实现泛型接口的非泛型类,Simple实现了两个IMyIfc实例。
1. 一个实例使用int类型实例化。
2. 一个实例使用string类型实例化。
5.3 泛型接口的实现必须唯一
- 实现泛型类型接口时,必须保证类型实参的组合不会在类型中产生两个重复的接口。
interface IMyIfc<T>
{
T ReturnIt(T inValue);
}
class Simple<S> : IMyIfc<int>, IMyIfc<S> // @1 错误写法
{
...
}
说明:
1. 在@1处,如果把int作为类型实参来代替第二个接口中S的话,Simple可能会有两个相同类型的接口,这是不允许的。
补充说明:泛型接口的名字不会和非泛型接口冲突,例如IMyIfc<>泛型接口和IMyIfc接口可以在程序中同时存在。
结尾
书籍:C#图解教程
著:【美】丹尼尔 · 索利斯;卡尔 · 施罗坦博尔
译:窦衍森;姚琪琳
ISBN:978-7-115-51918-4
版次:第5版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

浙公网安备 33010602011771号