泛型——
泛型定义:定义时使用类型占位符T,泛型就延伸出——泛型类,泛型方法,泛型委托,泛型接口。因此在定义以上类型时,就在方法、类、委托、接口 名称后<T> T:占位符 。
泛型思想:实际类型的指定,是推迟到调用时去指定实际类型——推迟一切可推迟的。
为了更好的理解什么时候使用泛型,我们观察下列方法——
/// <summary>
/// 将字符串转换成整型
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public int ToInt(string str)
{
int re = 0;
int.TryParse(str, out re);
return re;
}
/// <summary>
/// 将字符串转换为float
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public float ToFloat(string str)
{
float re = 0;
float.TryParse(str, out re);
return re;
}
/// <summary>
/// 将字符串转换为decimal
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public decimal ToDecimal(string str)
{
decimal re = 0;
decimal.TryParse(str, out re);
return re;
}
/// <summary>
/// 将字符串转换为double
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public double ToDouble(string str)
{
double re = 0;
double.TryParse(str, out re);
return re;
}
从以上方法可以看出,我们对字符串进行转换时的方法逻辑是一样的,只是返回的数据类型不同。那为了减少代码的书写,我们就可以将以上方法写成一个方法。——
public T ToType<T>(string str){
return Convert.ChangeType(str,typeof(T));
}
然后当我们想调用时——
int val1= model.ToType<int>("123");//这里自动推算,调用时必须显式加上<int>
float val2= model.ToType<float>("34.6");
因此得出一句话,泛型方法的使用场景——方法具有相似的逻辑,但是传入的参数类型或返回的类型不同。调用时,传入具体类型和参数。
泛型类
我们定义一个类
public class UserInfo
{
public int Id
{
get;
set;
}
public int UserAge
{
get; set;
}
public string Name
{
get; set;
}
}
同样的,泛型类也是相似的逻辑——我们定义一个泛型类——(类似于List<T>)
public class MyList<T> {
List<T> lst=new List<T>();
public int size{
get{
return lst.Count;
}
}
public void Add<T>(T t){
lst.Add(t);
}
public void Remove<T>(T t){
lst.Remove(t);
}
}
在应用泛型类时
//应用泛型类
MyList<UserInfo> userList = new MyList<UserInfo>();
userList.Add(new UserInfo() { Id = 1, Name = "Jsey" });
userList.Add(new UserInfo() { Id = 2, Name = "Whitle" });
以上就将UserInfo这个类型以 Id = 1, Name = "Jsey"添加到userList中,我们可以这样调用UserInfo user=userList[1]。也可以直接使用userList[1]。
泛型接口
泛型接口的定义与调用——
//泛型接口
public interface IBaseModel<T>
{
void Add(T t);
void Delete(T t);
T GetModel(int id);
}
接口类的具体实现——
//泛型接口的实现类:泛型类
class BaseModelNew<T> : IBaseModel<T>
{
public void Add(T t)
{
Console.WriteLine($"Add {t.GetType().Name}");
}
public void Delete(T t)
{
Console.WriteLine($"Delete {t.GetType().Name}");
}
public T GetModel(int id)
{
Console.WriteLine($"GetModel {typeof(T).Name}");
return default(T);
}
}
在主函数中的调用——先定义一个类型UserInfo user=userList[1];
{ IBaseModel<UserInfo> iUser = new BaseModelNew<UserInfo>();
iUser.Add(user);
iUser.Delete(user);
iUser.GetModel(1);
}
泛型委托详见Func和Action
简单了解——泛型的约束——
1)值类型约束——T:struct
//1)T:struct 值类型约束 2)引用类型约束 T:class ,1)和2)不能同时用
// 3)无参构造函数约束 new() 若与其他约束一起使用,只能在最后
//4)基类约束
//5) 接口约束 应用实际类型必须实现该接口 T:IBase
public class MyList<T> where T:struct
{
List<T> childs = new List<T>();
public int Size
{
get
{
return childs.Count;
}
}
public void Add(T t)
{
childs.Add(t);
}
public void Remove(T t)
{
childs.Remove(t);
}
}
有了T:struct后,我们在定义和使用这个类时就有了相对应的约束即——定义这个类时的<>中的内容必须是值类型才行。
// 值类型
MyList<int> myIds = new MyList<int>();
myIds.Add(12);
myIds.Add(34);
myIds.Remove(12);
//myIds.Add(23.5);
MyList<double> myIds1 = new MyList<double>();
2)引用类型约束——T:class
如果我们将struct改成class,则在定义和使用时,<>中的类型必须时引用类型。
//1)T:struct 值类型约束 2)引用类型约束 T:class ,1)和2)不能同时用
// 3)无参构造函数约束 new() 若与其他约束一起使用,只能在最后
//4)基类约束
//5) 接口约束 应用实际类型必须实现该接口 T:IBase
public class MyList<T> where T:class
{
List<T> childs = new List<T>();
public int Size
{
get
{
return childs.Count;
}
}
public void Add(T t)
{
childs.Add(t);
}
public void Remove(T t)
{
childs.Remove(t);
}
}
//引用类型
MyList<StudentInfo> myStuList = new MyList<StudentInfo>();//引用类型约束时,正确的
MyList<UserInfo> userList = new MyList<UserInfo>();
3)无参构造函数约束——new()
//1)T:struct 值类型约束 2)引用类型约束 T:class ,1)和2)不能同时用
// 3)无参构造函数约束 new() 若与其他约束一起使用,只能在最后
//4)基类约束
//5) 接口约束 应用实际类型必须实现该接口 T:IBase
public class MyList<T> where T:class,new()
{
List<T> childs = new List<T>();
public int Size
{
get
{
return childs.Count;
}
}
public void Add(T t)
{
T t1 = new T();//可以创建一个类型的实例(有了无参构造函数约束才行)
childs.Add(t);
}
public void Remove(T t)
{
childs.Remove(t);
}
}
有了以上的无参构造函数的约束,我们就不能够在使用这个函数的时候使用
//引用类型
MyList<StudentInfo> myStuList = new MyList<StudentInfo>();//引用类型约束时,正确的
MyList<UserInfo> userList = new MyList<UserInfo>();
MyList<string> strList = new MyList<string>(); // 它对new() 无效
使用上面最后一句的时候,会报错——
有了无参构造函数的约束,可以在当前类下创建实例。
4)基类约束
创建一个公有类——
public class ModelBase
{
public int Id { get; set; }
public string Name { get; set; }
}
在一个基础类中创建一个方法——
//基类约束 不能既是引用约束又是基类约束
public void ShowInfo<T>(T t) where T: ModelBase,IBase
{
Console.WriteLine($"Id:{t.Id} Name:{t.Name}");
}
将UserInfo修改成——
public class UserInfo : ModelBase
{
}
在调用时,我们就会有约束了——<>中的类型需要时ModelBase派生出来的
BaseModel model = new BaseModel();
UserInfo user = new UserInfo() { Id = 1, Name = "Jsey" };
不能既是基类约束,又是引用类型约束
接口约束不常用,作为了解使用,应用的实际类型必须实现该接口。
public class UserInfo : ModelBase, IBase
{
public void Show()
{
Console.WriteLine("SHow Info");
}
}
于是我们在使用的时候,就会有限制——
model.ShowInfo<UserInfo>(user);
model.ShowInfo<string>("sss");//不行 string 不是ModelBase派生而来的
创建基类约束的时候,内容可以什么都不写,它其实就是用来限制哪个泛型T的类型(该基类的派生类)