65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 在 ASP.NET 中使用泛型的示例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (29投票s)

2014年7月4日

CPOL

6分钟阅读

viewsIcon

158699

downloadIcon

1331

本文解释了泛型相比于非泛型的用处,并解释了如何使用泛型来重用代码。

引言

像您一样的开发者都熟悉面向对象编程,并理解它提供的优势。面向对象编程的一大优势是代码重用,通过创建基类并在派生类中继承它。派生类可以简单地覆盖虚方法或添加新方法来定制基类的行为以满足您的需求。泛型是公共语言运行时 (CLR) 和编程语言提供的另一种机制,它提供了一种代码重用和算法重用的形式。

泛型提供了一个代码模板,用于在不引用特定数据类型的情况下创建类型安全的代码。泛型允许您在编译时实现类型安全。它们允许您创建数据结构,而无需绑定到特定数据类型。然而,当使用数据结构时,编译器会确保与它一起使用的类型在类型安全方面是一致的。在通过示例进行解释之前,我想讨论泛型的两个好处,一个是类型安全,另一个是性能。

类型安全

泛型主要用于集合,但框架类库也有非泛型集合类,如 ArrayListHashtableSortedListStackQueue。因此,首先,我们看一个非泛型集合类的示例。换句话说,ArrayList,我们在其中将整数值添加到 ArrayList 并对列表值执行加法运算。

您需要创建一个类 ArrayListOperation,并定义一个 Addition 方法,如下面的代码片段所示。

using System.Collections;
namespace TypeSafety
{
    class ArrayListOperation
    {
        public static int Addition()
        {
            ArrayList list = new ArrayList();
            list.Add(5);
            list.Add(9);
 
            int result = 0;
            foreach (int value in list)
            {
                result += value;
            }
            return result;
        }
    }
}

当您运行此代码时,您将从方法中获得一个返回值。

正如您在前面的示例中看到的,有一个整数值的 ArrayList 并按预期获得结果,但现在向 ArrayList 添加一个新值,其数据类型为 float,并执行相同的 Addition 操作。让我们看下面的代码片段中的更新代码。

using System.Collections;
namespace TypeSafety
{
    class ArrayListOperation
    {
        public static int Addition()
        {
            ArrayList list = new ArrayList();
            list.Add(5);
            list.Add(9);
            list.Add(5.10);
 
            int result = 0;
            foreach (int value in list)
            {
                result += value;
            }
            return result;
        }
    }
}

在上面的代码中,所有三个值都可以轻松地添加到 ArrayList,因为 ArrayList 类的 Add() 方法的值是 object 类型,但在检索每个语句中的值时,每个值都会被赋给一个 int 数据类型变量。然而,这个 ArrayList 结合了整数和浮点类型的值,并且浮点值不会隐式转换为 int。这就是为什么代码会给出异常“指定的转换无效”。这意味着 ArrayList 不是类型安全的。这也意味着 ArrayList 可以被赋任何类型的值。

泛型允许您在编译时实现类型安全。它们允许您创建数据结构,而无需绑定到特定数据类型。然而,当使用数据结构时,编译器会确保与它一起使用的类型在类型安全方面是一致的。泛型提供类型安全,但没有任何性能损失或代码膨胀。System.Collections.Generics 命名空间包含泛型集合。现在让我们用泛型集合 List 看一个示例。

using System.Collections.Generic;
namespace TypeSafety
{
    class ListOperation
    {
        public static int Addition()
        {
            List list = new List();
            list.Add(5);
            list.Add(9);            
 
            int result = 0;
            foreach (int value in list)
            {
                result += value;
            }
            return result;
        }
    }
}

在上面的代码中,我们定义了一个 int 类型的列表。换句话说,我们只能向列表中添加整数值,而不能添加浮点值,所以当我们在 foreach 语句中使用列表中的值时,我们只得到一个 int 值。当您运行此代码时,您将从方法中获得一个返回值。现在您可以说泛型集合是类型安全的。

性能

在讨论性能之前,我们需要了解 C# 中的 object 数据类型。那么 object 数据类型是什么?来自 MSDN:“object 类型是 .NET Framework 中 Object 的别名。在 C# 的统一类型系统中,所有类型,无论是预定义的还是用户定义的,引用类型还是值类型,都直接或间接继承自 Object。您可以将任何类型的值赋给 object 类型的变量。当值类型变量转换为 object 时,称为装箱。

当 object 类型变量转换为值类型时,称为拆箱。”

我希望这有道理。

在泛型出现之前,定义通用算法的方法是将其所有成员定义为 Object 数据类型。如果您想使用该算法处理值类型实例,CLR 需要在调用算法成员之前将值类型实例进行装箱。这种装箱会在托管堆上产生内存分配,导致更频繁的垃圾回收,从而损害应用程序的性能。

由于现在可以创建泛型算法来处理特定的值类型,因此值类型的实例可以按值传递,CLR 不再需要进行任何装箱。此外,由于不需要进行类型转换,CLR 也无需检查尝试进行的类型转换的类型安全性,这也能提高代码速度。

现在让我们看一下非泛型和泛型集合类的 Add() 方法的语法。我们正在讨论两个集合类,一个是用于非泛型 (ArrayList),另一个是用于泛型 (List)。

ArrayList 的 Add() 方法的语法

 public virtual int Add(object value);

前面一行方法签名表示要添加到 ArrayList 的每个值都将是 object 类型,换句话说,如果您使用值类型添加到 ArrayList,那么在添加到 ArrayList 之前,它将被转换为 object 类型。

List<T>Add() 方法的语法

public void Add(T item);

前面一行方法签名表示向泛型列表添加项时无需进行类型转换。如果 List 类型是 T,那么项就是 T 类型。

现在您明白了谁的性能更好。泛型集合类的性能优于非泛型集合类。让我们在下面的代码片段中看一个示例。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace Performance
{
    class Program
    {
        static void Main(string[] args)
        {
            NonGenericPerformance();
            GenericPerformance();
            Console.ReadKey();
        }
        static void NonGenericPerformance()
        {
            long operationTime = 0;
            ArrayList arraylist = new ArrayList();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 1; i <= 100000; i++)
            {
                arraylist.Add(i);
            }
            operationTime = sw.ElapsedMilliseconds;
            Console.WriteLine("Array List {0} values add time is {1} milliseconds", arraylist.Count, operationTime);
            sw.Restart();
            foreach (int i in arraylist)
            {
                int value = i;
            }
            operationTime = sw.ElapsedMilliseconds;
            Console.WriteLine("Array List {0} values retrieve time is {1} milliseconds", arraylist.Count, operationTime);
        }
        static void GenericPerformance()
        {
            long operationTime = 0;
            List<int> list = new List<int>();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 1; i <= 100000; i++)
            {
                list.Add(i);
            }
            operationTime = sw.ElapsedMilliseconds;
            Console.WriteLine("List {0} values add time is {1} milliseconds", list.Count, operationTime);
            sw.Restart();
            foreach (int i in list)
            {
                int value = i;
            }
            operationTime = sw.ElapsedMilliseconds;
            Console.WriteLine("List {0} values retrieve time is {1} milliseconds", list.Count, operationTime);
        }
    }
}

让我们运行上面的代码。结果如图 1.1 所示。

图 1.1 泛型与非泛型集合之间的性能对比。

如何使用泛型重用代码?

在这里,我将介绍一个使用泛型进行代码重用的简单概念。我将使用一个通用方法来填充 UI 列表(下拉列表、复选框、单选按钮)。所以,让我们看代码。

首先,创建一个名为 Master 的表,其中包含 Id、Title 和 Type(UI 实体类型)这三个字段。让我们看用于表创建和数据库插入的数据库脚本。

 Create Table Master
(
Id int identity(1,1) primary key,
Title nvarchar(50) not null,
Type int not null
)
 
Insert Into Master values ('Jaipur',1),('Jhunjhunu',1),
('Cricket',2),('Football',2),('Male',3),('Female',3)

现在,根据我们的表单设计要求,为 UI 创建一个类型枚举。

 namespace GenericUIList
{
    public enum Types
    {
        NativePlace = 1,
        Hobby = 2,
        Gender = 3
    }
}

这里枚举的属性值与数据库表 Master 中的 Type 字段值相同。

在代码隐藏中编写连接字符串,例如:

<connectionStrings>   
    <add name ="genericConn" connectionString="Data Source=SANDEEPSS-PC;database=Development;user=sa;password=******"/>
</connectionStrings>

现在设计一个表单,其中包含用于 NativePlace(DropDownList)、Hobbies(复选框列表)和 Gender(单选按钮列表)的三个列表,如下面的代码片段所示。

<p>Native Place : <asp:DropDownList ID="ddlNativePlace" runat="server"></asp:DropDownList></p>
<p>Hobbies : <asp:CheckBoxList ID="chkHobbies" runat="server"></asp:CheckBoxList></p>
<p>Gender : <asp:RadioButtonList ID="rbGender" runat="server"></asp:RadioButtonList></p>

现在我们在代码隐藏文件 (.cs) 中创建一个泛型方法,并在页面加载时调用该方法。让我们看 Web 窗体的 .cs 页面的以下代码片段。

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
 
namespace GenericUIList
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                PopulateUIList<DropDownList>(ddlNativePlace, Types.NativePlace);
                PopulateUIList<CheckBoxList>(chkHobbies, Types.Hobby);
                PopulateUIList<RadioButtonList>(rbGender, Types.Gender);
            }
        }
        public void PopulateUIList<T>(T list, Types type) where T : ListControl
         {
             string connectionString = ConfigurationManager.ConnectionStrings["genericConn"].ConnectionString;
             using (SqlConnection con = new SqlConnection(connectionString))
             {
                 if(con.State == ConnectionState.Closed)
                 {
                     con.Open();
                 }
                 string cmdText = "Select Id,Title from Master Where Type = @type";
                 using (SqlCommand cmd = new SqlCommand(cmdText, con))
                 {
                     cmd.Parameters.Add(new SqlParameter("@type",(int)type));
                     DataTable dt = new DataTable();
                     IDataReader dr = cmd.ExecuteReader();
                     dt.Load(dr);
                     list.DataSource = dt;
                     list.DataTextField = "Title";
                     list.DataValueField = "Id";
                     list.SelectedIndex = 0;
                     list.DataBind();
                 }
             }
         }
    }
}

运行应用程序,然后在页面上看到如图 1.2 所示的结果。

图 1.2 填充 UI 列表

结论

我希望您现在能理解本文中泛型相比于非泛型的用处。我们还解释了如何使用泛型重用代码。如果您有任何疑问,可以在此处评论,或者直接通过 https://twitter.com/ss_shekhawat 与我联系。

© . All rights reserved.