泛型

泛型准确的来说是通用类型,通常作为一个方法模板和类型的模板泛型类和泛型方法兼具可重用性、类型安全性和效率,这是非泛型类和非泛型方法无法实现的。

泛型表示:List<T>,T是一个占位符,我们在Dynamic介绍过动态类型一般在执行的时候知道他的数据类型,那么T在编译的时候他就确定了它的类型。

命名空间 :System.Collections.Generic

我们经常在一个项目框架中泛型应该是用得最多了,那么它能给我们带来那些有利的功能呢?

泛型优点:

1.在泛型未出来之前,实现通用类型一般都是用object基类型来做通用类型强制转换,所以集合每次操作的时候都会拆箱装箱操作,而泛型就很好的解决了这个问题。

2.由于每次操作集合的时候都需要把数据转换成object类型,在编译之前我们是检测不到数据的类型,因为都是object基类型,所以在编译的时候我们无法预测到这个类型带来的风险

 

关于泛型使用的范围:

泛型类

class Student<T>{}
class Teacher<T,U>{}

泛型接口

interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }

泛型方法

void DoWork() { }
void DoWork<T>() { }
void DoWork<T, U>() { }

泛型类型

List<int> list = new List<int>() { 1, 2, 3 };
List<string> listStr = new List<int>() { "zhangsan", "lisi", "wangmazi" };

泛型委托

public delegate void Del<T>(T item);

泛型参数的约束:

约束说明
where T : struct类型参数必须是值类型。 可以指定除  Nullable<T> 以外的任何值类型。
where T : class类型参数必须是引用类型。 此约束还应用于任何类、接口、委托或数组类型
where T : unmanaged类型参数不能是引用类型,并且任何嵌套级别均不能包含任何引用类型成员。
where T : new()类型参数必须具有公共无参数构造函数。 与其他约束一起使用时,new() 约束必须最后指定
where T : <基类名>类型参数必须是指定的基类或派生自指定的基类。
where T : <接口名称>类型参数必须是指定的接口或实现指定的接口。 可指定多个接口约束。 约束接口也可以是泛型。
where T : U为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

注意:某些约束是互斥的。 所有值类型必须具有可访问的无参数构造函数。 struct 约束包含 new() 约束,且 new()约束不能与 struct 约束结合使用。 unmanaged 约束包含 struct 约束。 unmanaged 约束不能与 struct 或 new() 约束结合使用。

约束多个参数:

class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new()
{ }

未绑定的类型参数:

没有约束的类型参数(如公共类 SampleClass<T>{} 中的 T)称为未绑定的类型参数。 未绑定的类型参数具有以下规则:
不能使用 != 和 == 运算符,因为无法保证具体的类型参数能支持这些运算符。
可以在它们与 System.Object 之间来回转换,或将它们显式转换为任何接口类型。
可以将它们与 null 进行比较。 将未绑定的参数与 null 进行比较时,如果类型参数为值类型,则该比较将始终返回 false。

类型参数作为约束:

public class List<T>
{
    public void Add<U>(List<U> items) where U : T 
    {}
}

非托管约束:

从 C# 7.3 开始,可使用 unmanaged 约束来指定类型参数必须为“非托管类型”。 “非托管类型”不是引用类型,且任何嵌套级别都不包含引用类型字段。 通过 unmanaged 约束,用户能编写可重用例程,从而使用可作为内存块操作的类型,如以下示例所示:

unsafe public static byte[] ToByteArray<T>(this T argument) where T : unmanaged
{
    var size = sizeof(T);
    var result = new Byte[size];
    Byte* p = (byte*)&argument;
    for (var i = 0; i < size; i++)
        result[i] = *p++;
    return result;
}

委托约束:

同样从 C# 7.3 开始,可将 System.Delegate 或 System.MulticastDelegate 用作基类约束。 CLR 始终允许此约束,但 C# 语言不允许。 使用 System.Delegate 约束,用户能够以类型安全的方式编写使用委托的代码。

Action first = () => Console.WriteLine("this");
Action second = () => Console.WriteLine("that");

var combined = first.TypeSafeCombine(second);
combined();

Func<bool> test = () => true;
//必须是相同委托签名类型
//var badCombined = first.TypeSafeCombine(test);

枚举约束:

从 C# 7.3 开始,还可指定 System.Enum 类型作为基类约束。 CLR 始终允许此约束,但 C# 语言不允许。 使用 System.Enum 的泛型提供类型安全的编程,缓存使用 System.Enum 中静态方法的结果

enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}
var map = EnumNamedValues<Rainbow>();

泛型可变性:

协变性:协变性指的是——泛型类型参数可以从一个派生类隐式转化为基类。用out修饰,方法只能返回这个T类型

IEnumerable<string>strings=new List<string>();

逆变性:逆变性指的是——泛型类型参数可以从一个基类隐式转化为派生类,用in修饰,方法只能传入一个T类型

并不是所有类型都支持泛型的协变和逆变的,下面列出泛型的协变和你逆变中值得注意和明 确的地方:
1.    只有接口和委托支持协变和逆变(如Func<out    TResult>,Action<in    T>),类或泛型方 法的类型参数都不支持协变和逆变。
2.    协变和逆变只适用于引用类型,值类型不支持协变和逆变(因为可变性存在一个引用转换, 而值类型变量存储的就是对象本身,而不是对象的引用),所以List<int>无法转化为 Ienumerable<object>.
3.    必须显示用in或out来标记类型参数。
4.    委托的可变性不要再多播委托中使用,相信这点很多人都没有注意到的,    下面我举个例子 来说明下,当大家遇到这样的问题可以知道为什么:
上面代码可以通过编译,因为泛型Func<out    T>支持协变,所以将Func<string>转换为 Func<object>类型,但是对象本身仍然为Func<string>类型,然而Delegate.Combine方 法要求参数必须为相同类型——否则该方法无法确定要创建什么类型的委托(是 Func<string>类型呢还是Func<object>?),所以上面代码在运行时会抛出 ArgumetException(错误信息为——委托必须具有相同的类型)。我们可以稍微修改下上 面代码来使其不出现运行时错误

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
为什么我们要使用通用DAO接口呢,因为我们的数据库操作无非是增删改查,CRUD操作,我们不需要为每个实体去编写一个dao接口,对于相似的实体操作可以只编写一个通用接口,然后采用不同的实现! DAO已经成为持久层的标准模式,DAO使结构清晰,面向接口编程为代码提供了规范。而泛型DAO是一个类型安全的,代码精简的设计模式(相对于传统DAO),尤其在DAO组件数量庞大的时候,代码量的减少更加明显。 泛型DAO的核心是定义一个GenericDao接口,声明基本的CRUD操作: 用hibernate作为持久化解决方案的GenericHibernateDao实现类,被定义为抽象类,它提取了CRUD操作,这就是简化代码的关键,以便于更好的重用,这个就不给例子了,增删改都好写,查就需要各种条件了。 然后是各个领域对象的dao接口,这些dao接口都继承GenericDao接口,这样各个领域对象的dao接口就和传统dao接口具有一样的功能了。 下一步是实现类了,个自领域对象去实现各自的接口,还要集成上面的抽象类,这样就实现了代码复用的最大化,实现类中只需要写出额外的查询操作就可以了。当然还要获得域对象的Class实例,这就要在构造方法中传入Class实例。用spring提供的HibernateTemplate注入到GenericHibernateDao中,这样在各个实现类就可以直接调用HibernateTemplate来实现额外的查询操作了。 如果在实现类中不想调用某个方法(例如:update()),就可以覆盖它,方法中抛出UnsupportedOperationException()异常。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值