泛型基础

1.为什么要用泛型类,我们先来看一段代码:

以交换两个数为例,可能需要定义多个重载方法
public static void swap(ref int x,ref int y)
{
int temp = x;x=y;y=temp;
}
public static void swap(ref float x,ref float y)
{
float temp = x;x=y;y=temp;
}
public static void swap(ref double x,ref double y)
{
double temp = x;x=y;y=temp;
}
这些方法的实现代码类似,只是操作的数据类型不同而已,这种方法虽然没错,但工作效率低,可维护性差。一但发现错误,就不得不在个个地方重复修改。于是有人就会提出一种新的方法,既然C#中object类是其他任何类的基类,那么把方法中的参数类型改为object类型,不就可以交换任何类型的值了?
public static void swap(ref object x,ref object y)
{
object temp = x;x=y;y=temp;
}
这种方法虽然有可取之处,但又会带来新的麻烦,因为如果对值类型调用此方法,程序需要在值类型与object类之间进行装箱和拆箱的转换,频繁使用会大大降低程序的性能,另外,如果用户试图交换两个不同类型的值(例如一个×××,一个字符串),但这两种类型是不能交换的,程序能通过编译,但运行时会出错。
我们来看下面的代码:
using System;
namespace p9
{
public class abc
{
public static void Main()
{
int a = 10,b = 20;double c = 0.3,d = 1.25;string e = "我爱";string f ="你们";
fanxing<int>.swap(ref a,ref b);
fanxing<double>.swap(ref c,ref d);
Console.WriteLine("{0} {1},{2} {3},{4}{5}",a,b,c,d,e,f);
}
}
public class fanxing<T>
{
public static void swap(ref T x,ref T y)
{
T temp=x;x=y;y=temp;
}
}
}
上面定义的fanxing<T>就是一个泛型类,<>中的T被称为类型参数,类型参数T可以被不同的具体类型所替代(泛型类中每一个T都将被相应类型替代),fanxing<int>等被称为泛型类的构造类型。在Main()方法中,如: fanxing<int>.swap(ref a,ref b); 使用的不是泛型类本身,而是泛型类的构造类型fanxing<int>,泛型类的构造类型和其他类一样可以通过类型名来调用其静态成员,或使用new来创建对象,并通过对象来调用其实例成员。

2.泛型的静态成员

就如其他普通类中的静态成员(字段和方法)只能被类名(不是类的实例)调用一样,泛型类中的静态成员也只能被静态类的构造类名(不是泛型类名)调用。如果上述代码中出现以下代码就会出错:fanxing<int> a = new fanxing<int>(); a.swap(ref a,ref b); fanxing<T>.swap(ref a,ref b);
对于普通类,一个静态字段在内存中最多只有一个备份,对于泛型类,为其创建了多少种(不是多少个)构造类型,一个静态字段就在内存中有多少份备份。和普通类的静态构造函数在类创建实例时先于其他构造函数执行且只执行一次一样,泛型类的静态构造函数也先于其他构造函数执行,不同的是对于不同类型的构造类型(每种构造类型)都要调用一次静态构造函数,而对于其他构造函数,每一种构造类型有几个构造类型就调用几次构造函数。不同类型的构造类型之间不互相影响。如下代码:
using System
namespace p
{
public class abc
{
public static void Main()
{
Contact<int> c1 = new Contact<int>("赵丽");
Contact<int> c2 = new Contact<int>("汤姆");
Contact<double> c3 = new Contact<double>("李明"); }
}
}
public class Contact<T>
{
private string m_name;
private static int m_objects = 0;
private static int m_classes = 0;
public Contact (string name)
{
Console.WriteLine("构造对象:"+name);
m_name = name;
m_objects++;
Console.WriteLine("Contact<{0}>对象数量:{1}",typeof(T),m_objects);
}
static Contact()
{
Console.WriteLine("构造类Contact<{0}>",typeof(T));
m_classes++;
Console.WriteLine("Contact<{0}>类数量:{1}",typeof(T),m_classes);
}
}
输出结果为:
构造类Contact<System.Int32>
Contact<System.Int32>类数量:1
构造对象:赵丽
Contact<System.Int32>对象数量:1
构造对象:汤姆
Contact<System.Int32>对象数量:2
构造类Contact<System.Double>
Contact<System.Double>类数量:1
构造对象:李明
Contact<System.Double>对象数量:1

操作符重载

重载操作符的意思就是,重新定义该操作符的运算方法,类似于声明一个方法,也有形式参数。重载操作符要用到关键字operator。C#中重载操作符的语法是: public static 返回类型 operator 操作符 (形式参数)。注意:重载运算符时,必须用到public和static关键字,并且形式参数不能为空。
重载一元操作符
可重载的一元操作符有: + - ! ~ ++ -- true false (其中true与false的重载必须同时出现,且返回值类型是bool)
using System;
class BooleanInt
{
private int i;
public int Int
{
get {return i;}
set {i = value;}
}
public BooleanInt()
{}
public BooleanInt(int i)
{Int = i;}
public BooleanInt(BooleanInt bi)
{Int = bi.Int;}
public static BooleanInt operator +(BooleanInt bi)
{return new BooleanInt(bi.Int);}
public static BooleanInt operator -(BooleanInt bi)
{return new BooleanInt(-bi.Int);} // @@@@@
public static bool operator !(BooleanInt bi)
{if (bi.Int ==0)
return true;
return false;}
public static BooleanInt operator~(BooleanInt bi)
{return new BooleanInt( ~bi.Int);}
public static BooleanInt operator ++(BooleanInt bi)
{return new BooleanInt(++bi.Int);}
public static BooleanInt operator --(BooleanInt bi)
{return new BooleanInt(--bi.Int);}
public static bool operator true(BooleanInt bi)
{
if (bi.Int==0)
return false;
return true;
}
public static bool operator false(BooleanInt bi)
{
if (bi.Int==0)
return true;
return false;
}
}
class Test
{
public static void Main()
{
BooleanInt bi = new BooleanInt(10);
Console.WriteLine("bi:{0}",bi.Int);
bi=+bi;
Console.WriteLine("+bi:{0}",bi.Int);
bi=-bi;
Console.WriteLine("-bi:{0}",bi.Int);
Console.WriteLine("!bi:{0}",!bi);
Console.WriteLine("!!bi:{0}",!!bi);
bi=~bi;
Console.WriteLine("~bi:{0}",bi.Int);
BooleanInt newBi = ++bi;
Console.WriteLine("newBi = ++bi:{0},{1}",newBi.Int,bi.Int);
newBi = --bi;
Console.WriteLine("newBi = --bi:{0},{1}",newBi.Int,bi.Int);
bi = new BooleanInt(1);
if (bi) //调用true重载
Console.WriteLine("true - BooleanInt(1):true");
else
Console.WriteLine("true - BooleanInt(1):false");
bi =new BooleanInt(0);
if (bi) //也是调用true重载,只是true操作符的重载必须也同样有false操作符的重载
Console.WriteLine("false - BooleanInt(0):true");
else
Console.WriteLine("false - BooleanInt(0):false");
Console.Read();
}
}
输出结果:
bi:10
+bi:10
-bi:-10
!bi:False
!!bi:True
~bi:9
newBi = ++bi:10,10
newBi = --bi:9,9
true - BooleanInt(1):true
false - BooleanInt(0):false
@@@@@处,return new BooleanInt(-bi.Int); 等同于 BooleanInt a =new BooleanInt(-bi.Int);return a; 就是说先创造一个类的实例,同时调用该类的构造函数,再返回该类的对象,返回值取决于BooleanInt类的ToString()方法的字符串返回值。 例如一下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{

Console.WriteLine(abc.xie());
Console.ReadLine();
}
}
public class abc
{
public override string ToString() // ToString()是object类的一个方法,每个类都继承了该方法,如果子类不重写该方法,该方法返回的是当前类名的字符串形式。
{
Console.Write("ToString()方法用于将数值转换成字符串,本例为:");
return 123.ToString();
}
public abc(int a)
{
Console.WriteLine("return a,返回对象取决于该对象的ToString()方法");
}
public static abc xie()
{

abc a = new abc(1);
return a;
}
}
}
输出结果为:
return a,返回对象取决于该对象的ToString()方法
ToString()方法用于将数值转换成字符串,本例为:123
如果把return 123.ToString();改为return base.ToString(); 那么输出结果为:
return a,返回对象取决于该对象的ToString()方法
ToString()方法用于将数值转换成字符串,本例为: ConsoleApplication1.abc
也就是说ToString()方法前面有两种参数,一是简单值类型(这时是将简单值类型转换为字符串形式),一是base(这时base指的是基类,在此就是object类,而object类定义的 ToString()方法返回的是当前类名的字符串形式)

重载二元操作符
可重载的二元操作符有: + - * / % & | ^ << >> == != > < >= <= (其中==与!= >与< >=与<=的重载必须同时出现,如果重载了==与!=,最好同时重载Equals(),而这又需要重载GetHashCode()。如果重载了 + - * / % & | ^ << >>,那么也就重载了对应的复合值操作符 += -= *= /= %= &= |= ^= <<= >>=)
例如一下代码:
using System;
class BooleanInt
{
private int i;
public int Int
{
get{return i;}
set{i = value;}
}
public BooleanInt(){}
public BooleanInt(int i)
{Int = i;}
public BooleanInt(BooleanInt bi)
{Int = bi.Int;}
public static BooleanInt operator +(BooleanInt bi1,BooleanInt bi2)
{return new BooleanInt(bi1.Int + bi2.Int);}
public static BooleanInt operator -(BooleanInt bi1,BooleanInt bi2) //@
{return new BooleanInt(bi1.Int + bi2.Int);} /@@
public static bool operator ==(BooleanInt bi1,BooleanInt bi2)
{return bi1.Int==bi2.Int; }
public static bool operator !=(BooleanInt bi1, BooleanInt bi2)
{ return bi1.Int != bi2.Int; }
public override bool Equals(object obj)
{
return base.Equals(obj); //虽然是重载了,但等于没重载,因为内部仍然用的是基类(这里是object类)的Equals()方法,不过这样不会出现警告。
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
class test
{
public static void Main()
{
BooleanInt bi1 = new BooleanInt(8);
BooleanInt bi2 = new BooleanInt(2);
Console.WriteLine("bi1:{0} bi2:{1}",bi1.Int,bi2.Int);
BooleanInt bi = bi1 + bi2;
Console.WriteLine("bi1 + bi2 ={0}",bi.Int);
bi -= bi1; //由于上面@处重载了-,所以-=也被相应的重载了。
Console.WriteLine("bi -= bi1:{0}",bi.Int); //验证了,重载是重新定义操作符的算法,@@处虽然重载了-,但重载的算法是加法。
Console.WriteLine("bi!=bi1:{0}",bi!=bi1);
Console.Read();
}
}
重载转换运算符
语法: public static (implicit|explicit) operator 返回类型(当前类名 变量){具体转换}
implicit声明转换符为隐式 explicit声明转换符为显式

using System;
class BooleanInt
{
private int i;
public int Int
{
get{return i;}
set{i = value;}
}
public BooleanInt(){}
public BooleanInt(int i)
{Int = i;}
public BooleanInt(BooleanInt bi)
{Int = bi.Int;}
public static explicit operator int(BooleanInt bi) //重载(int) 重载后(int)就能把BooleaInt类型的值显式的转换成int类型了。
{return (int)bi.Int;}
}
class test
{
public static void Main()
{
BooleanInt bi = new BooleanInt(8);
int a= (int)bi;
Console.WriteLine(a);
Console.Read();
}
}

http://blog.csdn.net/kendezhu/article/details/4915129

转载于:https://blog.51cto.com/flydragon0815/674582

泛型与操作符重载杂谈相关推荐

  1. C#笔记09 结构、枚举、异常、泛型、操作符重载、dll、垃圾回收与资源清理、XML注释

    文章目录 结构体struct 枚举enum 异常Exception 执行try最近的最贴切的catch 继承Exception以定义异常 泛型 泛型的约束where 操作符重载 类型转换操作符重载 d ...

  2. python中线程安全的数据结构_Scala(八)-①-数据结构-集合操作-线程安全的集合-操作符重载...

    ① 集合操作 Why 为什么需要集合操作?集合操作都包括哪些?Scala的集合操作主要为了适应大数据的发展,我们以Map为例.于事需入局,于程需入题,先看下题. 入题 请将list(3,5,7) 中的 ...

  3. C++拾趣——有趣的操作符重载

    操作符重载是C++语言中一个非常有用的特性.它可以让我们比较优雅的简化代码,从而更加方便的编写逻辑. 为什么要使用操作符重载 一种常见的用法是重载<<运算符,让标准输出可以输出自定义的类型 ...

  4. 操作符重载——C/C++学习笔记

    此篇文章来自于网上,作为自己学习中的笔记,若有侵权行为,请告之,24小时之内必删除!下面就转入正题吧! 一.什么是操作符重载? 一看到重载,很容易就让人联想到成员函数重载,函数重载可以使名称相同的函数 ...

  5. C++——构造函数(拷贝构造,拷贝复制),析构函数,操作符重载

    C++--构造函数(拷贝构造,拷贝复制),析构函数,操作符重载 构造函数与析构函数:: 涉及构造函数还可以看这篇文章C++搞懂深拷贝初始化=与赋值=的区别 1.声明和定义构造函数和析构函数 构造函数在 ...

  6. 【Groovy】map 集合 ( map 集合操作符重载 | 使用 << 操作符添加一个元素 | 代码示例 )

    文章目录 一.使用 " << " 操作符添加一个元素 二.代码示例 一.使用 " << " 操作符添加一个元素 对 map 集合 使用 ...

  7. 【Groovy】map 集合 ( map 集合操作符重载 | *. 展开操作符 | 代码示例 )

    文章目录 一.map 集合 " *. " 展开操作符 二.代码示例 一.map 集合 " *. " 展开操作符 对 map 集合使用 " *. &qu ...

  8. 【Groovy】map 集合 ( map 集合操作符重载 | - 操作符重载 | 代码示例 )

    文章目录 一.map 集合 " - " 操作符重载 二.完整代码示例 一.map 集合 " - " 操作符重载 对 map 集合 使用 " - &qu ...

  9. 【Groovy】map 集合 ( map 集合操作符重载 | + 操作符重载 | 代码示例 )

    文章目录 一.map 集合 " + " 操作符重载 二.代码示例 一.map 集合 " + " 操作符重载 对 map 集合使用 " + " ...

最新文章

  1. mysql数据库 中文乱码_在CMD中操作mysql数据库出现中文乱码解决方案
  2. cocos2d-x 从onEnter、onExit、 引用计数 谈内存泄露问题
  3. php中浮点型的精确度,PHP中的浮点精度和类型
  4. 20220202--CTF刷题MISC方向--第5题--反编译
  5. linux c 禁止对外通信,Linux C套接字:在recv调用时被阻止
  6. java 排序api_用java api进行sort
  7. MongoDB入门 - 安装教程
  8. 大数据_MapperReduce_从CSV文件中读取数据到Hbase_自己动手实现Mapper和Reducer---Hbase工作笔记0021
  9. javascript学习笔记之document对象、表单及表单元素、脚本化cookie
  10. EMNLP'21 | 多语言和跨语言对话推荐
  11. 李宏毅 Gradient Descent总结
  12. JS向NPAPI传递参数,并返回结果
  13. 设计模式-(8)外观(swift版)
  14. R语言 安装包时出现的一个错误
  15. 学习笔记(01):19年录制Zookeeper、Dubbo视频教程 微服务教程分布式教程 SpringBoot教程整合-技术选型和学后水平...
  16. java随机点名_javascript实现的一个随机点名功能
  17. 使用java编写中国象棋(内含源代码)
  18. win7怎样更改计算机密码,win7系统电脑怎么设置开机密码
  19. 【git学习】解决git提交代码时报错: Unable to create ‘XXX/.git/index.lock‘: File exists.
  20. Opencv批量修改图片尺寸并保存

热门文章

  1. js解析url query_js如何解析url
  2. java web 跨域_java web服务端CORS跨域配置
  3. 数据结构实验之串三:KMP应用
  4. 【c++】29.设计模式总结
  5. 如何自学Android
  6. java list循环中删除元素的坑
  7. 白话阿里巴巴Java开发手册(编程规约)
  8. HTTPS协议原理分析
  9. Spring MVC快速入门
  10. 鸟哥的Linux私房菜(基础篇)- 第六章、Linux 的文件权限与目录配置