C#中泛型的概念详解

73 篇文章 3 订阅
46 篇文章 0 订阅

什么是泛型?

C# 语言和公共语言运行时 (CLR) 在 2.0 版本中添加了泛型。泛型将类型参数的概念引入 .NET Framework,这样就可以设计具有相同特征的类和方法:在客户端代码声明并初始化这些类和方法之前,这些类和方法会延迟指定一个或多个类型(使用占位符~3:数字代表类型参数个数)。

泛型有什么用?

使用泛型类型可以最大限度地重用代码、保护类型安全性以及提高性能。泛型最常见的用途是创建集合类 List<int>。

可以创建:泛型接口、泛型类、泛型方法、泛型事件和泛型委托。

泛型约束有哪些?

  where T : struct   类型参数必须是值类型。 

  where T : class    类型参数必须是引用类型。 此约束还应用于任何类、接口、委托或数组类型。

  where T : unmanaged   类型参数不能是引用类型,并且任何嵌套级别均不能包含任何引用类型成员。

  where T : new()     类型参数必须具有公共无参数构造函数。

  where T : <基类名>    类型参数必须是指定的基类或派生自指定的基类。

  where T : <接口名称>  类型参数必须是指定的接口或实现指定的接口。

  where T : U     为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

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

泛型类:

class GenericClass<T> { }

泛型接口:

public interface GenericInterface<T>{}

泛型方法:(可以作为传入参数,也可以作为返回值)

public T void Create<T>(T t)
{
    return default(T)
}

泛型委托:

public delegate void GenericDelegate<T>(T t);

实例使用泛型来种树(模拟下蚂蚁森林种树):

最开始的时候森林里只有梭梭树:

各个类代码:

复制代码

    public class SuosuoTree
    {
        //种植梭梭树需要的能量
        public int needEnergy()
        {
            return 17900;
        }
    }

复制代码

复制代码

    public class People
    {
        //姓名
        public string name { get; set; }

        //能量
        public int energy { get; set; }

        public void Plant(SuosuoTree tree)
        {
            if(energy< tree.needEnergy())
            {
                Console.WriteLine("能量不足");
            }
            {
                energy = energy- tree.needEnergy();
                Console.WriteLine($"恭喜{name}, 梭梭树种植成功,获得成就!!");
            }
        }
    }

复制代码

复制代码

    public class People
    {
        //姓名
        public string name { get; set; }

        //能量
        public int energy { get; set; }

        public void Plant(SuosuoTree tree)
        {
            if(energy< tree.needEnergy)
            {
                Console.WriteLine("能量不足");
            }
            {
                energy = energy- tree.needEnergy;
                Console.WriteLine($"恭喜{name}, 梭梭树种植成功,获得成就!!");
            }
        }
    }

复制代码

复制代码

    class Program
    {
        static void Main(string[] args)
        {
            People xiaoming = new People
            {
                name = "小明",
                energy = 200000
            };

            xiaoming.Plant(new SuosuoTree());

            Console.WriteLine("剩余能量:"+xiaoming.energy);

            Console.Read();

        }
    }

复制代码

输出结果为:

现在森林里可以种其他的树了(柠条,樟子松)。那我们添加2个类,修改People类,和Main方法,那有什么办法可以不修改People类的呢?

现在我们创建一个抽象类TreeBase:

复制代码

    public abstract class TreeBase
    {
        public abstract string GetTreeName();
        public abstract int needEnergy();
    }

    public class SuosuoTree: TreeBase
    {
        //种植梭梭树需要的能量
        public override int needEnergy()
        {
            return 17900;
        }
        public override string GetTreeName()
        {
            return "梭梭树";
        }
    }

    public class NingTiaoTree : TreeBase
    {
        //种植柠条需要的能量
        public override int needEnergy()
        {
            return 16930;
        }
        public override string GetTreeName()
        {
            return "柠条";
        }
    }

    public class ZhangZiSongTree : TreeBase
    {
        //种植樟子松需要的能量
        public override int needEnergy()
        {
            return 146210;
        }
        public override string GetTreeName()
        {
            return "樟子松";
        }
    }

复制代码

重新构造后的People:修改后添加了新的树苗,就不用修改People类了。

复制代码

    public class People
    {
        //姓名
        public string name { get; set; }

        //能量
        public int energy { get; set; }

        public void Plant<T>(T tree) where T:TreeBase
        {
            if(energy< tree.needEnergy())
            {
                Console.WriteLine("能量不足");
            }
            else
            {
                energy = energy- tree.needEnergy();
                Console.WriteLine($"恭喜{name},{tree.GetTreeName()}种植成功,获得成就!!");
            }
        }
    }

复制代码

小明也可以种不同的树了:

复制代码

    class Program
    {
        static void Main(string[] args)
        {
            People xiaoming = new People
            {
                name = "小明",
                energy = 200000
            };

            xiaoming.Plant(new SuosuoTree());
            xiaoming.Plant(new NingTiaoTree());
            xiaoming.Plant(new ZhangZiSongTree());

            Console.WriteLine("剩余能量:"+xiaoming.energy);

            xiaoming.Plant(new ZhangZiSongTree());

            Console.Read();

        }
    }

复制代码

什么是泛型?

C# 语言和公共语言运行时 (CLR) 在 2.0 版本中添加了泛型。泛型将类型参数的概念引入 .NET Framework,这样就可以设计具有相同特征的类和方法:在客户端代码声明并初始化这些类和方法之前,这些类和方法会延迟指定一个或多个类型(使用占位符~3:数字代表类型参数个数)。

泛型有什么用?

使用泛型类型可以最大限度地重用代码、保护类型安全性以及提高性能。泛型最常见的用途是创建集合类 List<int>。

可以创建:泛型接口、泛型类、泛型方法、泛型事件和泛型委托。

泛型约束有哪些?

  where T : struct   类型参数必须是值类型。 

  where T : class    类型参数必须是引用类型。 此约束还应用于任何类、接口、委托或数组类型。

  where T : unmanaged   类型参数不能是引用类型,并且任何嵌套级别均不能包含任何引用类型成员。

  where T : new()     类型参数必须具有公共无参数构造函数。

  where T : <基类名>    类型参数必须是指定的基类或派生自指定的基类。

  where T : <接口名称>  类型参数必须是指定的接口或实现指定的接口。

  where T : U     为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。

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

泛型类:

class GenericClass<T> { }

泛型接口:

public interface GenericInterface<T>{}

泛型方法:(可以作为传入参数,也可以作为返回值)

public T void Create<T>(T t)
{
    return default(T)
}

泛型委托:

public delegate void GenericDelegate<T>(T t);

实例使用泛型来种树(模拟下蚂蚁森林种树):

最开始的时候森林里只有梭梭树:

各个类代码:

复制代码

    public class SuosuoTree
    {
        //种植梭梭树需要的能量
        public int needEnergy()
        {
            return 17900;
        }
    }

复制代码

复制代码

    public class People
    {
        //姓名
        public string name { get; set; }

        //能量
        public int energy { get; set; }

        public void Plant(SuosuoTree tree)
        {
            if(energy< tree.needEnergy())
            {
                Console.WriteLine("能量不足");
            }
            {
                energy = energy- tree.needEnergy();
                Console.WriteLine($"恭喜{name}, 梭梭树种植成功,获得成就!!");
            }
        }
    }

复制代码

复制代码

    public class People
    {
        //姓名
        public string name { get; set; }

        //能量
        public int energy { get; set; }

        public void Plant(SuosuoTree tree)
        {
            if(energy< tree.needEnergy)
            {
                Console.WriteLine("能量不足");
            }
            {
                energy = energy- tree.needEnergy;
                Console.WriteLine($"恭喜{name}, 梭梭树种植成功,获得成就!!");
            }
        }
    }

复制代码

复制代码

    class Program
    {
        static void Main(string[] args)
        {
            People xiaoming = new People
            {
                name = "小明",
                energy = 200000
            };

            xiaoming.Plant(new SuosuoTree());

            Console.WriteLine("剩余能量:"+xiaoming.energy);

            Console.Read();

        }
    }

复制代码

输出结果为:

现在森林里可以种其他的树了(柠条,樟子松)。那我们添加2个类,修改People类,和Main方法,那有什么办法可以不修改People类的呢?

现在我们创建一个抽象类TreeBase:

复制代码

    public abstract class TreeBase
    {
        public abstract string GetTreeName();
        public abstract int needEnergy();
    }

    public class SuosuoTree: TreeBase
    {
        //种植梭梭树需要的能量
        public override int needEnergy()
        {
            return 17900;
        }
        public override string GetTreeName()
        {
            return "梭梭树";
        }
    }

    public class NingTiaoTree : TreeBase
    {
        //种植柠条需要的能量
        public override int needEnergy()
        {
            return 16930;
        }
        public override string GetTreeName()
        {
            return "柠条";
        }
    }

    public class ZhangZiSongTree : TreeBase
    {
        //种植樟子松需要的能量
        public override int needEnergy()
        {
            return 146210;
        }
        public override string GetTreeName()
        {
            return "樟子松";
        }
    }

复制代码

重新构造后的People:修改后添加了新的树苗,就不用修改People类了。

复制代码

    public class People
    {
        //姓名
        public string name { get; set; }

        //能量
        public int energy { get; set; }

        public void Plant<T>(T tree) where T:TreeBase
        {
            if(energy< tree.needEnergy())
            {
                Console.WriteLine("能量不足");
            }
            else
            {
                energy = energy- tree.needEnergy();
                Console.WriteLine($"恭喜{name},{tree.GetTreeName()}种植成功,获得成就!!");
            }
        }
    }

复制代码

小明也可以种不同的树了:

复制代码

    class Program
    {
        static void Main(string[] args)
        {
            People xiaoming = new People
            {
                name = "小明",
                energy = 200000
            };

            xiaoming.Plant(new SuosuoTree());
            xiaoming.Plant(new NingTiaoTree());
            xiaoming.Plant(new ZhangZiSongTree());

            Console.WriteLine("剩余能量:"+xiaoming.energy);

            xiaoming.Plant(new ZhangZiSongTree());

            Console.Read();

        }
    }

复制代码

/**********************************

C# 泛型(Generic)

泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。

您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。下面这个简单的实例将有助于您理解这个概念:

实例

using System;
using System.Collections.Generic;

namespace GenericApplication
{
    public class MyGenericArray<T>
    {
        private T[] array;
        public MyGenericArray(int size)
        {
            array = new T[size + 1];
        }
        public T getItem(int index)
        {
            return array[index];
        }
        public void setItem(int index, T value)
        {
            array[index] = value;
        }
    }
           
    class Tester
    {
        static void Main(string[] args)
        {
            // 声明一个整型数组
            MyGenericArray<int> intArray = new MyGenericArray<int>(5);
            // 设置值
            for (int c = 0; c < 5; c++)
            {
                intArray.setItem(c, c*5);
            }
            // 获取值
            for (int c = 0; c < 5; c++)
            {
                Console.Write(intArray.getItem(c) + " ");
            }
            Console.WriteLine();
            // 声明一个字符数组
            MyGenericArray<char> charArray = new MyGenericArray<char>(5);
            // 设置值
            for (int c = 0; c < 5; c++)
            {
                charArray.setItem(c, (char)(c+97));
            }
            // 获取值
            for (int c = 0; c < 5; c++)
            {
                Console.Write(charArray.getItem(c) + " ");
            }
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

0 5 10 15 20
a b c d e

泛型(Generic)的特性

使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:

  • 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
  • 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
  • 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
  • 您可以对泛型类进行约束以访问特定数据类型的方法。
  • 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。

泛型(Generic)方法

在上面的实例中,我们已经使用了泛型类,我们可以通过类型参数声明泛型方法。下面的程序说明了这个概念:

实例

using System;
using System.Collections.Generic;

namespace GenericMethodAppl
{
    class Program
    {
        static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp;
            temp = lhs;
            lhs = rhs;
            rhs = temp;
        }
        static void Main(string[] args)
        {
            int a, b;
            char c, d;
            a = 10;
            b = 20;
            c = 'I';
            d = 'V';

            // 在交换之前显示值
            Console.WriteLine("Int values before calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values before calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);

            // 调用 swap
            Swap<int>(ref a, ref b);
            Swap<char>(ref c, ref d);

            // 在交换之后显示值
            Console.WriteLine("Int values after calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values after calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);
            Console.ReadKey();
        }
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

Int values before calling swap:
a = 10, b = 20
Char values before calling swap:
c = I, d = V
Int values after calling swap:
a = 20, b = 10
Char values after calling swap:
c = V, d = I

泛型(Generic)委托

您可以通过类型参数定义泛型委托。例如:

delegate T NumberChanger<T>(T n);

下面的实例演示了委托的使用:

实例

using System;
using System.Collections.Generic;

delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
    class TestDelegate
    {
        static int num = 10;
        public static int AddNum(int p)
        {
            num += p;
            return num;
        }

        public static int MultNum(int q)
        {
            num *= q;
            return num;
        }
        public static int getNum()
        {
            return num;
        }

        static void Main(string[] args)
        {
            // 创建委托实例
            NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
            NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
            // 使用委托对象调用方法
            nc1(25);
            Console.WriteLine("Value of Num: {0}", getNum());
            nc2(5);
            Console.WriteLine("Value of Num: {0}", getNum());
            Console.ReadKey();
        }
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

Value of Num: 35
Value of Num: 175

  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值