zoukankan      html  css  js  c++  java
  • C#高级编程之泛型二(泛型类型约束、类型、反射与泛型)

    泛型类型约束

    简言之:对泛型类型进行约束,细化,限定。

    MSDN的定义:泛型定义中的 where 子句指定对用作泛型类型、方法、委托或本地函数中类型参数的参数类型的约束,意思就是可以有泛型类、泛型方法、泛型委托或泛型接口四类【即where可以写在这4种后面】。

     约束可指定接口、基类或要求泛型类型为引用、值或非托管类型。 它们声明类型参数必须具备的功能。意思就是where T,这个T类型参数可以是接口、基类、是引用类型、值类型或非托管类型。

                           下表列出了五种类型的约束:

    约束 说明
    T:struct 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。
    T:class 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。
    T:new () 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
    T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
    T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
    T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.

     

    在上一篇文章中我们提到对于以Object作为形参来解决方法复用的时候会有类型安全问题。泛型约束可以解决这一问题。具体如下例所示:

        class Animal
        {
            public string Name { get; set; }
    
            public void Say()
            {
                Console.WriteLine($"{Name} is saying");
            }
        }
        class Cat : Animal
        {
    
    
        }
        class Dog : Animal
        {
    
        }

    我们想让猫和狗发出叫声,当非动物叫的时候,程序抛异常了。客户端代码如下:

    public static void Main(string[] args)
            {
                Animal dog = new Cat()
                {
                    Name = "dogXiaoHe"
                };
                Animal Cat = new Cat()
                {
                    Name = "CatXiaoMiao"
                };
                string NonAnimalDemo = "123";
                ShowInfo(dog);
                ShowInfo(Cat);
                ShowInfo(NonAnimalDemo);
            }
        public static void ShowInfo(object obj)
            {
                Animal animal = (Animal)obj;//必须进行类型判断转换,因为C#是强类型语言,在运行时必须明确其类型。
                //dynamic animal = obj;
                //if(obj is Animal)
                //{
                //    (obj as Animal).Say();
                //}
                Console.WriteLine($"name={obj.GetType().Name},type={obj.GetType()},property Name={animal.Name}");
                animal.Say();
            }

    当执行到ShowInfo(NonAnimalDemo)时,程序抛异常,只能在运行期间才能捕捉到异常。

    注意一点:此处提供了几种进行类型转换的方法:

    1.直接强转,强转失败会抛异常。

    2.is as 类型转换,不会抛异常。

    3.dynamic关键字.dynamic是FrameWork4.0的新特性。dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再对类型进行检查(在代码后面加上animal.aaa();方法编译时也不会报错,但是不安全),编译期默认dynamic对象支持你想要的任何特性。

    换用泛型方法并进行泛型约束后:编译阶段就能检查错误。

        public static void ShowInfo<T>(T tParam) where T : Animal
            {
                Console.WriteLine($"name={tParam.GetType().Name},type={tParam.GetType()},property Name={tParam.Name}");
                tParam.Say();
            }

     

    泛型类型

    泛型接口

    如上面的例子,当只想让实现IRunning的动物Say时,可以使用泛型接口(或者这么理解,我让这个动物增加了一个running功能,拓展了功能),如下。

    class Tiger : Animal, IRunning
        {
            public void Speed()
            {
                Console.WriteLine($"{Name} speed is to fast,is runnning");
            }
        }

    泛型接口方法如下:

    public static void ShowInfo<T>(T tSParam) where T : Animal, IRunning
            {
                Console.WriteLine($"name={tSParam.GetType().Name},type={tSParam.GetType()},property Name={tSParam.Name}");
                tSParam.Say();
                tSParam.Speed();
            }

    客户端执行,结果如下:

    public static void Main(string[] args)
            {
                Animal dog = new Cat()
                {
                    Name = "dogXiaoHe"
                };
                Animal Cat = new Cat()
                {
                    Name = "CatXiaoMiao"
                };
                string NonAnimalDemo = "123";
                Tiger tiger = new Tiger()//注意此处的实例化必须是对Tiger
                {
                    Name = "tigerXiaoHu"
                };
                ShowInfo<Tiger>(tiger);
                //ShowInfo<Animal>(dog);会编译失败
                Console.ReadKey();
            }

    泛型类和泛型方法

    此处关于泛型方法有一点关键字default需要说明一下:

            /// <summary>
            /// 在不知道类型参数为值类型还是引用类型的情况下,为对象实例赋初值,T可能为引用类型;或者是值类型(数值或者结构体)
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="tParam"></param>
            /// <returns>
            /// 对于引用类型会返回 null,对于数值类型会返回零。
            /// 对于结构,此关键字将返回初始化为零或 null 的每个结构成员,具体取决于这些结构是值类型还是引用类型
            /// </returns>
            public static T ShowInfoWithReturn<T>(T tParam)
            {
                return default(T);
            }

    泛型事件

    泛型委托

    反射获取泛型类型

    通过反射我们可以获取程序集的相关信息,比如该程序集下有多少个类,每个类的属性、字段、方法等信息。那当遇到泛型如何进行相关操作呢。

    下面以反射执行某个类的方法为例讲解:

    泛型类

            /// <summary>
            /// 泛型类
            /// 执行GeneircInfo<T>类下的Show<T>方法
            /// </summary>
            static void ExecGenericClassMethod()
            {
                Type type = typeof(GeneircInfo<int>);
                //Type type = typeof(GeneircInfo<>);
                //Type[] actualType = { typeof(int) };
                //type = type.MakeGenericType(actualType);
                object instance = Activator.CreateInstance(type);
                //泛型类下的泛型方法
                type.InvokeMember("Show", BindingFlags.InvokeMethod, null, instance, new object[] { 123 });
            }
            /// <summary>
            ///泛型类
            /// GeneircClass<S,T>类下的ShowGeneircClassInfo<S,T>方法
            /// </summary>
            static void ExecuteGenricClass()
            {
                Type type = typeof(GeneircClass<,>);
                Type[] actualType = { typeof(string), typeof(int) };
                type = type.MakeGenericType(actualType);
                object instance = Activator.CreateInstance(type);
                type.InvokeMember("ShowGeneircClassInfo", BindingFlags.InvokeMethod, null, instance, new object[] { "abc", 456 });
            }

    泛型方法

        /// <summary>
            /// 泛型方法
            /// </summary>
            static void ExecuteGenericMethod()
            {
                Type type = typeof(GeneircMethod);
                object instance = Activator.CreateInstance(type);
                //常类下获取泛型方法
                MethodInfo method = type.GetMethod("Show", BindingFlags.Instance | BindingFlags.Public);
                //不加后面这句会报错:不能对 ContainsGenericParameters 为 True 的类型或方法执行后期绑定操作。”
                method = method.MakeGenericMethod(typeof(string));
                method.Invoke(instance, new object[] { "aaa" });
            }

    总结:

    加入泛型约束后,可以获取更多的功能,可以保证程序的安全性,避免错误调用。

    下次讲解协变、逆变。

  • 相关阅读:
    初识TPOT:一个基于Python的自动化机器学习开发工具
    为你的机器学习模型创建API服务
    iview 表格的多选框如何默认选中某项
    在uni-app中使用阿里巴巴图标库字体图标
    uni-app添加自定义底部导航栏,实现根据权限动态切换底部栏目的功能
    iview表格点击整行都可行切换前面的单选按钮或者多选按钮的状态
    uni-app引入vconsole调试移动端项目
    vue+view中的折叠面板点击表头阻止面板的收缩事件
    vue项目中判断文字内容超出n行显示详情按钮
    uni-app给顶部导航栏添加自定义字体图标
  • 原文地址:https://www.cnblogs.com/shuzhongke/p/13974817.html
Copyright © 2011-2022 走看看