C#基础系列——小话泛型

简介:

前言:前面两章介绍了C#的两个常用技术:C#基础系列——反射笔记 和 C#基础系列——Attribute特性使用 。这一章来总结下C#泛型技术的使用。据博主的使用经历,觉得泛型也是为了重用而生的,并且大部分时候会和反射一起使用。这次还是打算围绕WWH(即What、Why、How)来讲解。

 

1、什么是泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。怎么理解呢,其实根据博主的理解,泛型就是将类型抽象化,使用抽象化的类型或对象去实现某些功能和业务,然后所有需要使用这些功能和业务的具体类型去调用泛型的方法和委托。呵呵,是不是还是有点晕,别着急,我们来个例子:

我们首先来定义一种场景:我们通过sql语句使用Ado.Net来查询默认得到的是弱类型的DataTable、DataReader等,而我们需要对查询到的结果集使用lamada表达式进行某些复杂的计算,需要将DataTable转换为对应的List<T>集合,首先来定义一个泛型的方法:

复制代码
     public static List<T> GetListByDateTable<T>(DataTable dt)
        {
            List<T> modelList = new List<T>();
            try
            {
                //1.如果DataTable没有数据则直接返回
                if (dt == null || dt.Rows.Count == 0)
                {
                    return modelList;
                }

                //2.遍历DataTable填充实体
                var lstCol = dt.Columns;
                foreach (DataRow dr in dt.Rows)
                {
                    T model = default(T);
                    //如果是object(这种一般用于一个实体类表示不了的情况),则先拼接json再反序列化为object
                    if (typeof(T).Equals(typeof(object)))
                    {
                        var strJson = "{";
                        foreach(DataColumn oCol in lstCol)
                        {
                            var oAttrValue = Convert.IsDBNull(dr[oCol.ColumnName]) ? null : dr[oCol.ColumnName];
                            strJson += "\"" + oCol.ColumnName + "\":\"" + oAttrValue + "\",";
                        }
                        strJson = strJson.ToString().Trim(',') + "}";
                        model = E2ERes.JavaScriptStrToObj<T>(strJson);
                    }
                    else
                    {
                        model = FillEntityByDT<T>(dt, dr);
                    }
                    modelList.Add(model);
                }
            }
            catch
            { 
                
            }
            return modelList;
        }

        //通过DataTable填充实体类
        private static T FillEntityByDT<T>(DataTable dt, DataRow dr)
        {
            T model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });//反射得到泛型类的实体
            PropertyInfo[] pro = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            Type type = model.GetType();
            foreach (PropertyInfo propertyInfo in pro)
            {
                if (dt.Columns.Contains(propertyInfo.Name))
                {
                    if (Convert.IsDBNull(dr[propertyInfo.Name]))
                    {
                        continue;
                    }
                    if (!string.IsNullOrEmpty(Convert.ToString(dr[propertyInfo.Name])))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null);
                    }
                }
            }
            return model;
        }
复制代码

有了这个泛型的方法,我们在转换DataTable和具体的List<Model>的时候是不是就是一个很好的复用。

 

2、为什么要使用泛型:博主记得刚参加工作的前两年有一次面试的时候就被问到“泛型有什么优势?”,当时怎么回答的不记得了,只知道面试不太顺利~~为什么要用泛型呢?博主觉得泛型的主要优势有以下几点:

(1)保证了类型的安全性:泛型约束了变量的类型,保证了类型的安全性。例如List<int>和ArrayList。List<int>集合只能加入int类型的变量,ArrayList可以Add任何常用类型,编译的时候不会提示错误。

(2)避免了不必要的装箱、拆箱操作,提高程序的性能:泛型变量固定了类型,使用的时候就已经知道是值类型还是引用类型,避免了不必要的装箱、拆箱操作。举例说明:

使用泛型之前,我们使用object代替。

object a=1;//由于是object类型,会自动进行装箱操作。

int b=(int)a;//强制转换,拆箱操作。这样一去一来,当次数多了以后会影响程序的运行效率。

使用泛型之后

复制代码
public static T GetValue<T>(T a)

{
  return a;
}

public static void Main()

{
  int b=GetValue<int>(1);//使用这个方法的时候已经指定了类型是int,所以不会有装箱和拆箱的操作。
}
复制代码

(3)提高方法、算法的重用性。上面的例子基本能说明这个优势。

 

3、泛型的使用:

(1)泛型方法的使用:这也是博主使用最多的用法之一,像这种泛型方法一般是一些static的通用方法,例如原来项目中用到的一个将DataGridViewRow对象转换成对应的实体Model的方法如下:

复制代码
     public static T ToObject<T>(DataGridViewRow item) where T:class
        {
            var model = item.DataBoundItem as T;
            if (model != null)
                return model;
            var dr = item.DataBoundItem as System.Data.DataRowView;
            model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });//反射得到泛型类的实体
            PropertyInfo[] pro = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            Type type = model.GetType();
            foreach (PropertyInfo propertyInfo in pro)
            {
                if (Convert.IsDBNull(dr[propertyInfo.Name]))
                {
                    continue;
                }
                if (!string.IsNullOrEmpty(Convert.ToString(dr[propertyInfo.Name])))
                {
                    var propertytype = propertyInfo.PropertyType;
                    if (propertytype == typeof(System.Nullable<DateTime>) || propertytype == typeof(DateTime))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDateTime(dr[propertyInfo.Name]), null);
                    }
                    else if (propertytype == typeof(System.Nullable<decimal>) || propertytype == typeof(decimal))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDecimal(dr[propertyInfo.Name]), null);
                    }
                    else if (propertytype == typeof(System.Nullable<int>) || propertytype == typeof(int))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToInt32(dr[propertyInfo.Name]), null);
                    }
                    else if (propertytype == typeof(System.Nullable<double>) || propertytype == typeof(double))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDouble(dr[propertyInfo.Name]), null);
                    }
                    else
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null);
                    }
                }
            }
            return model;
        }
复制代码

使用泛型方法的注意事项:

  • 泛型方法的重载:public void Fun1<T>(T a);和public void Fun1<U>(U a);是无法重载的,这其实很好理解,因为T和U其实都是泛型的一个代表符号而已;
  • 泛型方法的重写:下面的方法重写FuncA的重写是正确的,FuncB的重写不正确,因为约束被默认继承,不用再写。
复制代码
abstract class BaseClass
{
    public abstract T FuncA<T,U>(T t,U u) where U:T;
    public abstract T FuncB<T>(T t) where T:IComparable;
}
 
class ClassA:BaseClass
{
    public override X FuncA<X,Y>(X x,Y y){...}
    public override T FuncB<T>(T t) where T:IComparable{...}
}
复制代码

 

(2)泛型类的使用:

public class Class_Base<DTO, T>
{}

使用这个类的时候必须要指定两个泛型类。

 

(3)泛型接口以及泛型继承的使用:

泛型接口的类型参数要么已实例化,要么来源于实现类声明的类型参数

public interface Interface_Base<T>
{}

public class Class_Base<DTO, T> : Interface_Base<DTO>
{}

DTO来源于实现类Class_Base

 

(4)泛型委托的使用:其实这种用法博主真的用得很少,只是原来见到过大牛们类似的代码。

定义泛型委托:

public delegate void MyDelegate<T>(T obj);

泛型委托的使用:

复制代码
public delegate void MyDelegate<T>(T obj);
static void Main(string[] args)
{
    var method = new MyDelegate<int>(printInt);
    method(1);
    Console.ReadKey();
}
static void printInt(int i)
{
    Console.WriteLine(i);
}
复制代码

 

(5)泛型约束:用来约束泛型类型有那些特性。常见的泛型约束也就那么几类:

泛型约束的格式

public class Imps_Base<DTO, T> : Ifs_Base<DTO>
        where T : BaseEntity
        where DTO : class
    {
  }

 

约束 说明

T:struct

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。

T:class

类型参数必须是引用类型,包括任何类、接口、委托或数组类型。

T:new()

类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。

T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.






本文转自懒得安分博客园博客,原文链接:http://www.cnblogs.com/landeanfen/p/4642820.html,如需转载请自行联系原作者

目录
相关文章
|
7月前
|
存储 安全 编译器
C# 11.0中的泛型属性:类型安全的新篇章
【1月更文挑战第23天】C# 11.0引入了泛型属性的概念,这一新特性为开发者提供了更高级别的类型安全性和灵活性。本文将详细探讨C# 11.0中泛型属性的工作原理、使用场景以及它们对现有编程模式的改进。通过深入了解泛型属性,开发者将能够编写更加健壮、可维护的代码,并充分利用C#语言的最新发展。
|
存储 算法 安全
C#三十二 泛型的理解和使用
C#三十二 泛型的理解和使用
39 0
|
3月前
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
87 11
|
4月前
|
编译器 C#
C#中内置的泛型委托Func与Action
C#中内置的泛型委托Func与Action
73 4
|
4月前
|
C#
C# 面向对象编程(三)——接口/枚举类型/泛型
C# 面向对象编程(三)——接口/枚举类型/泛型
38 0
|
7月前
|
存储 安全 Java
34.C#:listT泛型集合
34.C#:listT泛型集合
63 1
|
7月前
|
开发框架 安全 .NET
C# .NET面试系列三:集合、异常、泛型、LINQ、委托、EF!
<h2>集合、异常、泛型、LINQ、委托、EF! #### 1. IList 接口与 List 的区别是什么? IList 接口和 List 类是C#中集合的两个相关但不同的概念。下面是它们的主要区别: <b>IList 接口</b> IList 接口是C#中定义的一个泛型接口,位于 System.Collections 命名空间。它派生自 ICollection 接口,定义了一个可以通过索引访问的有序集合。 ```c# IList 接口包含一系列索引化的属性和方法,允许按索引访问、插入、移除元素等。 由于是接口,它只定义了成员的契约,而不提供具体的实现。类似于 IEnumera
367 2
|
7月前
|
存储 安全 算法
C# 泛型:类型参数化的强大工具
【1月更文挑战第7天】本文将深入探讨C#语言中的泛型编程,包括泛型的定义、用途、优势以及实际应用。通过类型参数化,泛型允许开发者编写更加灵活且可重用的代码,同时提高程序的类型安全性和性能。本文将通过示例代码和详细解释,帮助读者更好地理解泛型在C#中的重要性和实用性。
|
7月前
|
存储 Java 编译器
【从Java转C#】第五章:泛型
【从Java转C#】第五章:泛型
|
机器学习/深度学习 存储 缓存
一文带你搞懂C#泛型
泛型是.net 2.0中提供的新特性,是框架的一种升级,用于处理用一个事物来代替多种不同需求的情况。下面我们就一块来看一下具体的讲解吧。