Net is as typeof 运行运算符详解
概述
在了解运行运算符的前提我们需要了解什么是RTTI ,在任何一门面向对象的语言中,都有RTTI这个概念(即 运行时)。
RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
RTTI提供了以下两个非常有用的操作符:(1)typeid(C#中叫TypeOf)操作符,返回指针和引用所指的实际类型。(2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。面向对象的编程语言,像C++,Java,delphi,C#都提供了对RTTI的支持。 本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI;本文还将详细描述两个重要的 RTTI 运算符的使用方法, 下面我们来开始学习吧。 注:本文没有介绍赋值运算符、比较运算符、逻辑运算符、三目运行符等一二三元运算,这个在前面java 基础文章有提到,请参考本人Java基础或其他人的博客,没有基础运算符的理解,这个文章很难能看懂?
运行时类型标识
-
运行时标识有什么用呢?
1.运行时类型标识,能预先测试某个强制类型转换操作,能否成功,从而避免无效的强制类型转换异常。2. 在c#中有三个支持RTTI的关键字:is 、 as 、typeof。 下面依次介绍他们。
- IS运算符
通过is运算符,能够判断对象类型是否为特顶类型,如果两种类型是相同类型,或者两者之间存在引用,装箱拆箱转换,则表明两种类型是兼容的。
class Program { static void Main(string[] args) { People p1 = new People(); Person p2 = new Person(); if (p1 is People) { Console.WriteLine("p1 是 People 的对象 ~~~"); } if (p2 is People) { //这个打印,因为p2是Person类型的对象,而Person类型派生于People类型 //由于Person对象可以转换为People类型,因此Person对象与People类型是兼容的,但是反过来就不成立 Console.WriteLine("p2 是 People 的对象..."); } if (p1 is Person) { Console.WriteLine("p1 是 Person 的对象***"); } if (p2 is Person) { Console.WriteLine("p2 是 Person 的对象---"); } if (p1 is object) { Console.WriteLine("任何类的父类为object"); } Console.Read(); } } /// <summary> /// 个人信息 /// </summary> public class People { public string Name { get; set; } public int Age { get; set; } public string Sex { get; set; } } /// <summary> /// 人的群体信息 /// </summary> public class Person: People { public string Household { get; set; } }
as运算符:
在运行期间执行类型转换,并且能够使得类型转换失败不抛异常,而返回一个null值。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 | using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main( string [] args) { People p1 = new People() { Age=30,Sex= "女" ,Name= "mainaizi" }; Person p2 = new Person() { Age=29,Sex= "男" ,Name= "大棒槌" ,Household= "北京昌平" }; Person p = p1 as Person; //as类型先检查强制类型转换的有效性,如果有效,则执行强类型转换过程。这些都在这一句完成。 Console.WriteLine(p); Console.WriteLine( "清输入任意字符按Enter继续..." ); string s= Console.ReadLine(); try { Person person = (Person)p1; } catch (Exception exe) { Console.WriteLine(exe.Message); } People people = p2; Console.WriteLine(people); //这里执行的是自动转换 所以不需要强制类型转换,我们大类型转换小类型的转换叫做自动转换 Console.Read(); } } public class PersonAnws { /// <summary> /// 自定义隐士转换 /// </summary> /// <param name="v">被转化类型</param> public static implicit operator PersonAnws(People v) { People p = new People(); return p; } /// <summary> /// 自定义显示转换【这里不过多解释】 /// </summary> /// <param name="v"></param> public static explicit operator Int32(PersonAnws p) { return 0; } } /// <summary> /// 个人信息 /// </summary> public class People { public string Name { get ; set ; } public int Age { get ; set ; } public string Sex { get ; set ; } public override string ToString() { return "{" +$ "name:{this.Name},age:{this.Age},sex{this.Sex}" + "}" ; } } /// <summary> /// 人的群体信息 /// </summary> public class Person: People { public string Household { get ; set ; } public override string ToString() { return "{" + $ "name:{base.Name},age:{base.Age},sex{base.Sex},Household:{this.Household}" + "}" ; } } } |
typeof运算符:
as ,is 能够测试两种类型的兼容性。但大多数情况下,还需要获得某个类型的具体信息。这就用到了typeof,它可以返回与具体类型相关的System.Type对象,通过System.Type对象可以去顶此类型的特征。一旦获得给定类型的Type对象,就可以通过使用该对象定义的各种属性,字段,方法来获取类型的具体信息。Type类包含了很多成员,我们一起来看下吧。
using System;using System.Collections.Generic;using System.Data;namespace testData{ class Program { static void Main(string[] args) { Type type= typeof(Person); Console.WriteLine(type); Console.WriteLine(type.FullName);//完整名称 Console.WriteLine(type.Name);//类名称 Console.WriteLine(type.BaseType);//基类 Console.WriteLine(type.IsSealed);//是否为密封类 Console.WriteLine(type.IsPublic);//是否是共有的 Console.Read(); } } /// <summary> /// 个人信息 /// </summary> public class People { public string Name { get; set; } public int Age { get; set; } public string Sex { get; set; } public override string ToString() { return "{"+$"name:{this.Name},age:{this.Age},sex{this.Sex}"+"}"; } } /// <summary> /// 人的群体信息 /// </summary> public class Person: People { public string Household { get; set; } public override string ToString() { return "{" + $"name:{base.Name},age:{base.Age},sex{base.Sex},Household:{this.Household}" + "}"; } }}
说到typeOf我们不得不说下类型的装载
列如
Type er= Type.GetType(“System.String”);
Console.WriteLine(er.Name);
我们管这种的获取类的类型叫做配件的装载。配件装载只能在本程序集中进行搜索,也就是说,在本程序集的命名空间下进行搜索。
姓名:王柏成 英文名:WbcSky QQ:1477865775 电子邮件:w329357255@126.com
net 自定义泛型那点事
泛型概述
泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。
泛型定义
泛型的定义主要有以下两种:1.在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)2.在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(人们大多把这称作模板)不论使用哪个定义,泛型的参数在真正使用泛型时都必须作出指明。一些强类型编程语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的编程语言只能达到部分目的。
正文
假如我想输出double ,int 和 dateTime类型的类型名字和他们的值。现在需要定义方法如下
using System;using System.Data;namespace testData{ class Program { static void Main(string[] args) { ShowInt(2); ShowDateTime(DateTime.Now); ShowDouble(2.52); Console.Read(); } public static void ShowInt(int value) { Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}"); } public static void ShowDouble(double value) { Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}"); } public static void ShowDateTime(DateTime value) { Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}"); } }}
这样写起来,好像比较麻烦,我们可不可以用一个方法来代替呢???
我们很多初学者都接触过这两个泛型,
List<int> list = new List<int>();
Dictionary<int, string> dic = new Dictionary<int, string>();
第一个是列表,第二个我们称为字典,那么我们可不可以自己定义一个泛型方法呢????
泛型的语法结构及定义
泛型语法: 泛型类或方法的后面 “<T>” T 是占位符,可以是任意字母,主要代表一个类型,如果是方法,参数就应当为占位符参数。 如 Show<T>(T t);方法,User<T> 类 。
泛型的特点:1.延迟声明,2使用的时候在声明
1.泛型方法的定义和使用
using System;using System.Collections.Generic;using System.Data;namespace testData{ class Program { static void Main(string[] args) { Show<int>(1); Show<double>(1.904); Show<DateTime>(DateTime.Now); Show<string>("wbc"); Console.Read(); } public static void Show<T>(T t) { Console.WriteLine($"typeName:{t.GetType().Name},Value:{t}"); } }}
我们会发现,完全可以运行,一个方法就搞定.那么我们可不可以省略 <int> ,答案是肯定的,当没有涉及到装箱拆箱操作的时候,我们完全可以省略,如下
using System;using System.Collections.Generic;using System.Data;namespace testData{ class Program { static void Main(string[] args) { Show(1); Show (1.904); Show(DateTime.Now); Show("wbc"); User u = new User(); Show(u); Console.Read(); } public class User { } public static void Show<T>(T t) { Console.WriteLine($"typeName:{t.GetType().Name},Value:{t}"); } }}
有人会说了,我使用Object 作为参数,一样可以实现,为什么不使用object呢,一切类型的父类是object ,应当可以的啊??,答案是肯定的,也是可以的,知所以不使用,是因为使用object类型的时候,会产生装箱拆箱操作,这个操作会损失精度。
2泛型类的定义和使用
泛型类的语法和泛型方法的语法很像,只是关键字是class ,如 Public Class MyGer<T>{
}
那么我们来看下泛型类的使用,看如下案列:
using System;using System.Collections.Generic;using System.Data;namespace testData{ class Program { static void Main(string[] args) { Person p = new Person() { Name="zhangsan",Age=28,Id=1,Id_No="110000198212193145",Address="beijiang",Sex="女" }; MyGrenric<Person> gren = new MyGrenric<Person>(); gren.Show(p); Console.Read(); } } /// <summary> /// 人的父类 /// </summary> public class BasePopleModel { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } } public class Pople { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } public override string ToString() { string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}"; return base.ToString(); } } //人的信息 public class Person : BasePopleModel { public int Id { get; set; } public override string ToString() { string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }"; return result; } } public class MyGrenric<T> { public void Show(T t) { Console.WriteLine(t); } }}
看了上面的代码,我们可以看出,泛型类里面的方法,可以是普通方法和泛型方法,当普通方法使用泛型的时候,我们可以省略泛型,上诉代码替换为:
public void Show<T>(T t) { Console.WriteLine(t); }是一样的结果。但是泛型类也有一个弊端,那就是失去了继承类的继承性质,如
BasePopleModel p = new Person()
{
Name=”zhangsan”,Age=28,Id=1,Id_No=”110000198212193145″,Address=”beijiang”,Sex=”女”
};
MyGrenric<Person> gren = new MyGrenric<Person>();
gren.Show(p);
这个时候,我们的代码就会报错。那如何在泛型中保留类的基础性,使用如下语法就能做到:
当我们所有继承了BasePopleModel的子类,都可以使用这个泛型类。
3泛型接口的定义和使用
我们来看下代码,定义一个泛型接口,和定义泛型类的语法结构是一样的,他本身也具有接口的特性。代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ConsoleApp3{ public interface IBaseImpl<T> { int State { get; set; } void Show(T t); void Search<S>(S s,T t); }}
当我们普通类继承这个泛型接口的时候,我们会发现,继承不了,生成编译项目的时候,会提示出错误信息:如下图
那我们来试试,泛型类来继承泛型接口,看好使不好使,把我们上面创建的泛型类继承我们的接口(MyGrenric<T>:IBaseImpl<T>)如下:
public class MyGrenric<T>:IBaseImpl<T> { public int State { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public void Search<S>(S s, T t) { throw new NotImplementedException(); } public void Show(T t) { Console.WriteLine(t); } }
我们发现是能运行的.
现在问题来了,刚才我们的实例类继承泛型接口继承不了,我们的实例类继承泛型类能继承吗??,有没有什么办法,让我们的实例类继承我们的泛型接口和泛型类呢?我们先来看看,能不能实例类继承泛型类
我们会发现还是继承不了:既然前边说了,T只是一个占位符,我们可不可以显示的写出一个类呢??
我们发现,这样是可以的,没有编译错误,那么我泛型类和泛型接口的占位符都是T,那么我们使用不同 的类可以吗??,答案是否定的,绝对不可以,一个占位符只能代表一个类型,所以我们要使用不同的类型,就需要使用不同的占位符,如:
泛型约束
前边学习了这么多自定义泛型的知识,我们基本就把整个泛型学习完了,我们之前一直都是说T,S,是泛型的一个占位符,可以是任意类型,那我们可以限定这个类型吗?答案是肯定的,继续看图片
通过上诉,我们能看出,我们限定了类型,只能是Pople 类类型。限定语法,“只有在泛型类和接口之后跟WHERE 泛型占位符 :类型”。
where T: 类型值 | 说明 | 限定规范 |
class | 限定泛型只能是class 类型 | 可以有参数构造函数或无参数构造函数,不能和其他关键字一起使用 |
struct | 限定泛型只能是struct类型 | 不可以和其他类型一起使用 |
new() | 限定只能是类类型,切有无参数构造函数 | 必须有参数构造函数,不能和其他关键字一起使用 |
类类型 | 如果传入的是父类,则保留继承性质 | 无 |
值类型 | 无 | 无 |
这里就不过多演示上述内容,我们在这里只演示class ,代码如下
using System;using System.Collections.Generic;using System.Data;namespace testData{ class Program { static void Main(string[] args) { BasePopleModel p = new Person() { Name="zhangsan",Age=28,Id=1,Id_No="110000198212193145",Address="beijiang",Sex="女" }; MyGrenric<BasePopleModel> gren = new MyGrenric<BasePopleModel>(); gren.Show(p); Console.Read(); } } /// <summary> /// 人的父类 /// </summary> public class BasePopleModel { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } } public class Pople { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } public override string ToString() { string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}"; return base.ToString(); } } //人的信息 public class Person: BasePopleModel { public Person() { } public int Id { get; set; } public void Show(BasePopleModel s) { throw new NotImplementedException(); } public override string ToString() { string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }"; return result; } } public class MyGrenric<T> where T : class ,new() { public void Show(T t) { Console.WriteLine(t); } } //public interface IBaseImpl<S> where S : class //{ // int State { get; set; } // void Show(S s); //}}
总结:泛型类必须继承在泛型类上,如果作为普通类的父类,必须显示指定其类型,在约束的时候,泛型类不能指定值类型的约束。
泛型接口必须被泛型类和泛型接口继承,如果被普通接口和普通类继承,必须显示的指定类型,必须放在多个继承文件的最后。在约束的时候,不能使用new()。
本文只是介绍常用的泛型使用方向,很多其他方向没有详细介绍,常用就是泛型类的使用,并不怎么涉及到泛型的继承,如果一个项目涉及到泛型继承,证明这个项目也是快重构了。不过在开发过程中,泛型的约束是经常使用的。仅供参考
另付2.0以后提供的常用泛型如下:
2.0版的.NET框架类库提供了一个新的命名空间,System.Collections.Generic,其中包含了一些已经可以使用的泛型容器类和相关的接口。和早期版本的.NET框架提供的非泛型容器类相比,这些类和接口更高效且是类型安全的。在设计、实现自定义的容器类之前,请你考虑是否使用或继承所列出类中的一个。
下面的表格列出了新的泛型类和接口,旁边是对应的非泛型类和接口。在一些地方要特别注意,如List<T>和Dictionary<T>,新泛型类的行为(behavior)与它们所替换的非泛型类有些不同,也不完全兼容。更详细的内容,请参考System.Collections.Generic的文档
泛型类或接口 |
描述 |
对应的非泛型类型 |
Collection<T> ICollection<T> |
为泛型容器提供基类 |
CollectionBase ICollection |
Comparer<T> IComparer<T> IComparable<T> |
比较两个相同泛型类型的对象是否相等、可排序。 |
Comparer IComparer IComparable |
Dictionary<K, V> IDictionary<K,V> |
表示用键组织的键/值对集合。 |
Hashtable IDictionary |
Dictionary<K, V>.KeyCollection |
表示Dictionary<K, V>中键的集合。 |
None. |
Dictionary<K, V>.ValueCollection |
表示Dictionary<K, V>中值的集合。 |
None. |
IEnumerable<T> IEnumerator<T> |
表示可以使用foreach 迭代的集合。 |
IEnumerable IEnumerator |
KeyedCollection<T, U> |
表示有键值的集合。 |
KeyedCollection |
LinkedList<T> |
表示双向链表。 |
None. |
LinkedListNode<T> |
表示LinkedList<T>中的节点。 |
None. |
List<T> IList<T> |
使用大小可按需动态增加的数组实现 IList 接口 |
ArrayList IList |
Queue<T> |
表示对象的先进先出集合。 |
Queue |
ReadOnlyCollection<T> |
为泛型只读容器提供基类。 |
ReadOnlyCollectionBase |
SortedDictionary<K, V> |
表示键/值对的集合,这些键和值按键排序并可按照键访问,实现IComparer<T>接口。 |
SortedList |
Stack<T> |
表示对象的简单的后进先出集合。 |
Stack |
姓名:王柏成 英文名:WbcSky QQ:1477865775 电子邮件:w329357255@126.com