C#简单理解-泛型

什么是泛型:泛型就相当于一个模子,装入类型的材料,可以塑造成我们想要的产品。打个比方一个娃娃的模型,上面有个孔,注入金水,就是金娃娃,注入泥浆,就是泥娃娃。


T是类型,它在整个类的定义当中起到占位符的作用

怎么使用:
class Cage<T>{……}    泛型类的声明
Cage<Dog> dogCage;    Cage<Dog>类型的引用
dogCage=new Cage<Dog>();    构造实例

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var dogCage = new Cage<Dog>(1);
            dogCage.PutIn(new Dog("Jack"));
            dogCage.PutIn(new Dog("Herry"));

        }
    }

    class Pet
    {
        private string name;
        private int age;

        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }

        public Pet(string name)
        {
            this.Name = name;
        }
    }

    class Dog : Pet
    {
        public Dog(string name) : base(name) { }
    }

    class Cat : Pet
    {
        public Cat(string name) : base(name) { }
    }

    class Cage<T>
    {
        T[] array; // 定义一个T类型的数组,用来装宠物
        private readonly int size; // 笼子里能装入的宠物总数量
        private int num; // 装入笼子的宠物的数量
        public Cage(int n) // 构造函数时初始化
        {
            size = n;            
            num = 0;
            array = new T[size];
        }

        public void PutIn(T p)
        {
            if (num < size)
            {
                array[num++] = p;
            }
            else
                Console.WriteLine("笼子满了");
        }

        public T TakeOut()
        {
            if (num>0)
            {
                return array[--num];
            }
            else
            {
                Console.WriteLine("笼子里没有宠物");
                return default(T);
            }
        }
    }
}

泛型方法:
泛型类中可以有泛型方法,普通类中也可以有泛型方法
语法:
class Dog{
    void DogIsHappy<T>(T target){}
}
泛型参数可以用在参数列表,也可以用在返回值中,也可以在方法体中

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog("jack");
            dog.DogIsHappy<int>(7);            
            dog.DogIsHappy<Food>(new Food());
        }
    }

    class Pet
    {
        private string name;
        private int age;

        public string Name { get => name; set => name = value; }
        public int Age { get => age; set => age = value; }

        public Pet(string name)
        {
            this.Name = name;
        }
    }

    class Dog : Pet
    {
        public Dog(string name) : base(name) { }

        public void DogIsHappy<T>(T target) {
            Console.WriteLine(Name+"看见 "+target.ToString()+" 开心");
        }
    }

    class Food {
    }
}

假如我们有个需求,需要打印出传递的参数类型和参数值,我们可以这么写:
 

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowInt(7);
            ShowString("ABC");
            ShowDateTime(DateTime.Now);
        }

        /// <summary>
        /// 打印个int值
        /// 因为方法声明的时候,固定了参数类型
        /// </summary>
        /// <param name="iParameter"></param>
        public static void ShowInt(int iParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(Program).Name, iParameter.GetType().Name, iParameter);
        }

        /// <summary>
        /// 打印个string值
        /// </summary>
        /// <param name="sParameter"></param>
        public static void ShowString(string sParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(Program).Name, sParameter.GetType().Name, sParameter);
        }

        /// <summary>
        /// 打印个DateTime值
        /// </summary>
        /// <param name="oParameter"></param>
        public static void ShowDateTime(DateTime dtParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(Program).Name, dtParameter.GetType().Name, dtParameter);
        }
    }
}

可是我们发现除了传入参数不一样,其实处理方法是一样的,于是我们改成了Object类型
 

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Show(7);
            Show("ABC");
            Show(DateTime.Now);
        }

        public static void Show(Object iParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(Program).Name, iParameter.GetType().Name, iParameter);
        }
    }
}

这样,我们发现代码简洁了,也能达到同样的效果,但是用Object类型,涉及到装箱拆箱,装箱拆箱又涉及到会损耗程序的性能。
微软2.0推出泛型,可以很好的解决这一问题。
代码改写如下:

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Show(7);
            Show("ABC");
            Show(DateTime.Now);
        }

        public static void Show<T>(T t)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(Program).Name, t.GetType().Name, t);
        }
    }
}

泛型接口:定义跟泛型类类似 
泛型接口语法:
interface IMyself<T>
{
        T GetSome(T t);
}
实现泛型接口的语法:
class A : IMyself<A>
{
        public A GetSome(A t) { return default(A); }
}

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog();
            dog.study(new Sit());
        }
    }

    interface ILearn<T> { // 定义一个学习技能的泛型接口
        void study(T t);
    }

    class Dog : Pet,ILearn<Sit> // 狗狗类继承Pet类,并实现技能泛型接口
    {
        public void study(Sit t) {
            Console.WriteLine("学会坐下");
        }
    }

    class Sit { }
    class Pet {
    }
}

泛型的约束:缩小泛型类型的范围。我们定义了泛型,它可以传入值类型,也可以传入引用类型,范围很广。
约束的类型:类名(该类或者继承该类的类)    class(任何类)    struct(任何值)    接口名(该接口类型或者任何实现该接口的类型)    new()(带有无参共有构造函数的类)
约束叠加规则:A主约束(类名,class,struct)只能有1个    B接口约束(任意多个)    C构造约束
约束语法:
void Cage<T> where T:Pet,IclimbTree,new() 
{}
这个意思就是传入一个泛型参数T,对T进行约束,它必须是Pet类或Pet的派生类,必须实现IclimbTree接口,必须要有默认构造函数
class MyClassy<T, U> where T : class  where U : struct
{}
如果像上面一样,是多个泛型参数,where T 就是对T约束为class(只能输入任何类)  where U 就是对U约束为struct(只能输入值类型)

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog("jack");
            //dog.DogIsHappy<int>(4); // 传入值类型错误,因为DogIsHappy限制了,只能是Pet类或继承自Pet的类
            dog.DogIsHappy<Dog>(new Dog("Lucy"));
        }
    }

    class Pet {
        private string name;
        public string Name { get => name; set => name = value; }
        public Pet(string name)
        {
            Name = name;
        }
        public void PrintName() {
            Console.WriteLine(Name);
        }
    }

    class Dog : Pet {
        public Dog(string name) : base(name) { }
        public void DogIsHappy<T>(T t) where T:Pet { // 这里约束了传入的值只能是Pet类或者继承Pet的类
            Console.WriteLine("happy because:"+t.Name);
        }
    }
}

该篇文章前面部分有句代码:return default(T)
我们现在就讲讲default关键字
default关键字可以得到该类型的默认值
int a=0;  等价于  int a=default(int);
之所以会用到default关键字,是因为需要在不知道是值类型还是引用类型的情况下,给对象赋初值。

class TestDefault<T>
{
    public T foo()
    {
        T t = null; //???
        return t;
    }
}

事实上,我们并不知道T到底是引用类型还是值类型,上面这个代码就把它当引用类型处理的,但万一T是值类型呢?例如T是int类型,那注释的那一行这么写就是无意义的,为了解决这个问题,我们就引入了default关键字。

class TestDefault<T>
{
    public T foo()
    {       
        return default(T);
    }
}

参考:
C#泛型约束
C# default(T)关键字 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值