C# 高级编程 泛型

来源:互联网 发布:淘宝客拉人进群技巧 编辑:程序博客网 时间:2024/06/06 18:14

可以使用泛型 创建独立于被包含类型的类和 方法

不必给不同类型的类编写功能相同的许多方法和类,只创建一个方法或类 即可 

Object 类 不是类型安全的

泛型类使用泛型类型,并 可以根据需要用特定的类型  替换泛型类型 ,保证类型安全性 


泛型 不仅限于类,还有接口和方法

值类型 转换成引用类型 称为 装箱。 装箱 会自动操作 

引用类型 转换成 值 类型称为拆箱。拆箱 时 需要类型强制转换运算符

ArrayList 存储对象, 当存储值类型时  会进行装箱 和 拆箱 操作  ,损失性能

可以给这个集合中添加 任意类型

list.Add(32);
list.Add("asds");
list.Add(new Object());

而  List<T> 是泛型

只能添加 T 类型数据到 集合中 


泛型类 可以定义一次,并且可以用许多不同的类型实例化

在JIT 编译器 把泛型类 便以为本地代码时,会给每个值类型创建一个新类。

引用类型 共享同一个本地类的所有相同的实现代码

引用类型实例化的泛型类中  只需要4个字节的内存地址

值类型 对内存地址的要求都各不相同 ,所以要为每个值类型 实例化 提供一个新类

每个处理对象 类型 都可以有 泛型 实现方式。

另外 如果使用了层次结构,泛型 就非常有助于消除类型强制转换操作


不能把null 赋给泛型类型

default 关键字 根据上下文可以有多种含义。switch 语句 使用default 定义默认情况

在泛型中,根据 泛型类型是使用引用类型还是值类型,泛型 default 用于将 泛型类型初始化为null 或0

T doc = default(T);


如果泛型类需要调用泛型类型中的方法,就必须添加约束

class DocumentManager<T> where T:IDocument

where T: struct  对于结构约束,类型T必须是值类型 

where T: class 类型T 必须是引用类型

where T: IFoo 类型T必须实现接口IFoo

where T:Foo 类型T必须继承自基类Foo

where T: new()  类型T必须有一个默认构造函数

where T1:T2 类型T1派生自 类型T2

只能为默认构造函数定义构造函数约束,不能为其他构造函数定义构造函数约束


使用泛型类型可以合并多个约束

泛型类型 可以 实现泛型接口,也可以派生自一个类。

泛型类 可以 派生自泛型基类

要求是 必须是重复接口的泛型接口,或者必须指定基类的类型

派生类 可以 是泛型 类 和非泛型类


使用泛型 可以定义接口 ,在接口中定义的方法 可以带 泛型参数


协变和抗变 指对参数和返回值的类型进行转换    参数 协变,返回值抗变

支持泛型接口 和 泛型委托 的协变和抗变


如果泛型类型用out关键字标注,泛型接口就是协变的。意味着返回类型只能是T。???

接口IIndex 与 类型T是协变的,并从一个只读索引器中返回这个类型


.NET中, 唯一可变性的类型转换 就是 由 继承关系带来的  子类引用-> 父类引用 转换 

与原始类型转换方向相同的可变性 就是 协变


泛型类型 用in 关键字 标注,泛型接口就是抗变的。

这样,接口就只能把泛型类型T用作其方法的输入

一个可变性和子类到父类转换的方向一样,就称作协变;而如果和子类到父类的转换方向相反,就叫抗变!

支持协变和抗变的有两种类型:泛型接口和泛型委托 数组是不支持抗变得

声明属性时要注意,可读写的属性会将类型同时用于参数和返回值。

因此只有只读属性才允许使用out类型参数,只写属性能够使用in参数。


public interface ICovariant<out T> { }
public class Shape : ICovariant<Shape> { }
public class Rectangle : Shape, ICovariant<Rectangle> { }


class Program
{
static void Main(string[] args)
{
ICovariant<Shape> isShape = new Shape();
ICovariant<Rectangle> isRectangle = new Rectangle();
isShape = isRectangle;

如果接口不支持协变,即没有out 关键字,那么isShape = isRectangle;转换时,就会出现两种类型的T,不统一

添加 out 是使 接口中的T能够自动隐式转换

如果一个泛型接口IFoo<T>IFoo<TSub>可以转换为IFoo<TParent>的话,我们称这个过程为协变,而且说“这个泛型接口支持对T的协变”

协变是针对 泛型接口 T 的协变


泛型接口T 由子类都父类转换是协变 使用out,由父类到子类转换是抗变 使用 in

如果一个类型参数仅仅能用于函数的返回值,那么这个类型参数就对协变相容,用out修饰。

而相反,一个类型参数如果仅能用于方法参数,那么这个类型参数就对抗变相容,用in修饰。

如果一个接口需要对类型参数T协变,那么这个接口所有方法的参数类型必须支持对类型参数T的抗变(如果T有作为某些方法的参数类型)

http://blog.csdn.net/sudazf/article/details/17148971


数据库中的数字可以为空,而 C# 中的数字不能为空

Int32是一个结构,而结构的实现同值类型,所以结构不能为空  

Nullable<int> x;

int? x; 相同,都表示可空类型



泛型类型用方法声明来定义。泛型方法可以在非泛型中定义

调用泛型方法时,不需要显式填写泛型方法中的类型  

void Swap<T>(ref T x,ref T y);

int x=1;

int y=2;

Swap(ref x,ref y);

0

带约束的泛型方法

带约束的泛型方法


如何通过传递一个泛型委托来修改 Accumulate() 方法。

泛型委托 Func<T1,T2,TResult>

public static T2 Accmulates<T1,T2>(IEnumerable<T1> source,Func<T1,T2,T2> action)
{
T2 sum = default(T2);
foreach(T1 item in source)
{
sum = action(item, sum);
}
return sum;
}

调用泛型委托时,需要指定    泛型参数类型 ,因为编译器不能自动推断出 该类型

decimal amount1 = Algorithm.Accmulates<Account, decimal>(accounts, (item, sum) => sum += item.Balance);

泛型方法规范:

泛型方法可以重载,为特定类型定义规范。

在编译期间会决定调用哪个版本的方法,而不是运行期间


public void Foo<T>(T obj){}

public void Foo(int x){}

public void Bar<T>(T obj){ Foo(obj); }

test.Foo(22);//Foo(int x)

test.Bar(22);//Foo<T>(T obj)


泛型集合 可以去除集合中的算法
















原创粉丝点击