C#中Equals 与== 的区别
对于值类型来说,Equals与==两者比较的都是“内容”是否相同,即值是否一样,很显然此时两者是同一个意思。
对于引用类型来说,==(等号)比较的是两个变量的“引用” 是否一样,也就是比较引用的“地址”是否相同。而equals()仍然比较的是变量的 “内容” 是否一样。
以string为例
C# 泛型
优点:
1.使用泛型类、方法,我们可以极大提高代码的重用性,不需要对类型不同代码相同(仅类型参数不同)的代码写多次。
2.创建泛型类,可在编译时创建类型安全的集合
3.避免装箱和拆箱操作降低性能,在大型集合中装箱和拆箱的影响非常大.
值类型和引用类型
在 C#中 简单类型,结构类型,枚举类型是值类型;其余的:接口,类,字符串,数组,委托都是引用类型
byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。
string 和 class object统称为引用类型。
值类型存储在栈内存或堆内存之中,而引用类型只能放堆内存里。 栈比堆快 值类型快
- 值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
- 引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
- 值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
- 引用类型的对象总是在进程堆中分配(动态分配)
相同点:
引用类型可以实现接口,值类型当中的结构体也可以实现接口;
引用类型和值类型都继承自System.Object类。
不同点:
几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即 直接继承System.ValueType。即System.ValueType本身是一个类类型,而不是值类型。其关键在于ValueType重写了Equals()方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。
堆和栈装箱和拆箱
堆和栈:程序运行时的内存区域
我们把内存分为堆空间和栈空间。
栈空间比较小,但是读取速度快
堆空间比较大,但是读取速度慢
1.栈
栈的特征:
数据只能从栈的顶端插入和删除
把数据放入栈顶称为入栈(push)
从栈顶删除数据称为出栈(pop)
2. 堆是一块内存区域,与栈不同,堆里的内存能够以任意顺序存入和移除
3.值类型和引用类型
类型被分为两种:值类型(整数,bool struct char 小数)和引用类型(string 数组 自定义的类,内置的类)。
1)值类型只需要一段单独的内存,用于存储实际的数据,(单独定义的时候放在栈中)
2)引用类型需要两段内存
第一段存储实际的数据,它总是位于堆中
第二段是一个引用,指向数据在堆中的存放位置
装箱 就是把“值类型”转换成“引用类型”(Object);
拆箱 就是把“引用类型”转换成“值类型”;
//装箱 boxing int i = 3 ; //分配在栈上 object o = i ;//隐式装箱操作,int i 在堆上 object b = (object)i ; //显示装箱操作 //拆箱 unboxing int j = (int) o ;//显示拆箱(将对象o拆箱为int类型) int k = b ;//error!!, 不能隐式拆箱
拆箱 的操作包括
1,检查对象实例,以却确保它是给定值类型的装箱值。
2,将该值从实例复制到值类型变量中。
下面来看看这个例子:
int i=0;
System.Object obj=i;
Console.WriteLine(i+","+(int)obj);
其中共发生了3次装箱和一次拆箱!^_^,看出来了吧?!
第一次是将i装箱,第2次是输出的时候将i转换成string类型,而string类型为引用类型,即又是装箱,第三次装箱就是(int)obj的转换成string类型,装箱!
拆箱就是(int)obj,将obj拆箱!!
C#委托( 各种lambda )事件
委托最基本的意思就是自己干不了(自己干的无法满足全部需求),找个人来干,然后你给那个人给点材料(参数),下达一个结果标准(返回值),
A 请求 B 帮我获取或者传递的行为,就叫做委托, 委托就是把函数当变量,说白了就是传递一段代码到另一个函数内部,在另外一个函数里去调用
所有的异步都是委托 委托就是函数当入参 委托被各种语法糖遮蔽了 =>就是委托 匿名委托
随这net的不断升级,委托也出现了这三者。(Delegate、Action、Func)
应用场景比如跨线程更新winform UI,线程调用处理等等。
1,Delegate 委托关键字 ,实际是声明的方法原型 ,即参数和返回类型
tatic void Main(string[] args) { reslt = 0; DeleMeath deleMeathADD = new DeleMeath(add); //实例化委托,传入符合委托原型的方法名 int resOne = deleMeathADD(5, 6); Console.WriteLine("第一种方式的委托调用,结果是{0}", resOne); int resTwo = deleMeathADD.Invoke(1, 2); Console.WriteLine("第二种方式的委托调用,结果是{0}", resTwo); AsyncCallback asyncCallback = new AsyncCallback(AsyncCallbackMath); IAsyncResult result = deleMeathADD.BeginInvoke(1, 5, asyncCallback, null); //开始委托的异步,异步主要是不会阻塞线程 reslt = deleMeathADD.EndInvoke(result); //结束异步 //下面是多波委托,有点和事件附加类似 DeleMeath m, n, c, d; m = add; //加法 n = Remo; //减法 //c = m + n; //减法,委托n //c = n + m; //加法,委托m // c = m - n; //加法,委托n c = n - m; //加法,委托m Console.WriteLine("多播的m值为{0}", m(1, 2)); Console.WriteLine("多播的n值为{0}", n.Invoke(6, 2)); Console.WriteLine("多播的c值为{0}", c.Invoke(1, 2)); Console.ReadKey(); } static int reslt = 0; static void AsyncCallbackMath(IAsyncResult result) { if (result.IsCompleted) //回调 { Console.WriteLine("委托异步调用完成,计算结果是{0}", reslt); } } delegate int DeleMeath(int parmOne, int parmTwo); //声明一个委托 static int add(int a, int b) //符合委托 原型的方法 { return a + b; } static int Remo(int a, int b) //符合委托 原型的方法 { return a - b; }
2.比较老的 delege 委托,新的升级中有新的关键字 Action 和Func
2.1,Action,可以传入参数,没有返回值的委托!
看看函数重载说明就知道了!上代码,一目了然
static void Main(string[] args) { Action<int, int> action = new Action<int, int>(addVoid); // Action<int, int> 这就是方法的原型 action(1, 2); action.Invoke(2, 3); //基本的使用方法和delege都是差不多的,包括异步的写法也是相同的, //但是他升级了,厉害之处是加入了lamda, Action<int, int>就是声明委托原型 简化了写法,通过lamda (n,m)=>{} 匿名函数的写法 Action<int, int> actionOne = new Action<int, int>((n, m) => { Console.WriteLine("lamda方式1 计算结果{0}", (n + m)); }); actionOne.Invoke(4, 5); //lamda 搞法很优雅 Action<int, int> actionTwo = (n, m) => { Console.WriteLine("lamda方式2 计算结果{0}", (n + m)); }; actionTwo.Invoke(3, 4); Console.ReadKey(); } static void addVoid(int a, int b) { Console.WriteLine("计算结果{0}", (a + b)); }
2.2,Func用法,他跟Action 的区别是可以有返回值
out 这个是输出参数,简单来说就是我们的返回值,重载很多的时候,最后一个就是,可以敲看看
static void Main(string[] args) { Func<int> func = new Func<int>(ReturnOne); //一个参数时候,就是返回值(无参数,有返回值),Func<int>这就是方法的原型了 int res = func.Invoke(); Console.WriteLine(" 计算结果{0}", res); Func<int> funcOne = () => { return 1; }; int resOne = funcOne.Invoke(); Console.WriteLine("lamda方式1 计算结果{0}", res); Func<int> funcTwo = new Func<int>(() => { return 2; }); int resTwo = funcTwo.Invoke(); Console.WriteLine("lamda方式2 计算结果{0}", resTwo); Func<int, int, int> funcThree = (n, m) => { return n + m; }; int resThree = funcThree.Invoke(1, 2); //参数只两个,最后的一个参数也是int,不过是输出参数,也就是返回值 Console.WriteLine("lamda方式3 计算结果{0}", resThree); Func<int, int, int> funcFour = new Func<int, int, int>(add); int resFour = funcThree.Invoke(1, 3); //调用封装好的方法 Console.WriteLine("lamda方式4 计算结果{0}", resFour); Console.ReadKey(); } static int add(int a, int b) { return a + b; } static int ReturnOne() { return 1; } }
//无返回值,无参数委托,不需要单独声明 Action act = this.DoNothing;
//无返回值,有参数委托,参数类型为泛型 Action<string> act = p => { };
//返回类型为string,参数类型为string的委托 Func<string,string> func = p => p; //返回类型为bool,参数类型为string的委托 Func<string,bool> func = p => p.Equals('');
Demo
/// <summary> /// 扩展方法 /// </summary> public static class DelegateExtend { /// <summary> /// 模仿Linq的Where操作 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="scoure">数据源</param> /// <param name="func">委托(自定义bool条件)</param> /// <returns></returns> public static IEnumerable<T> ExtWhere<T>(this IEnumerable<T> scoure, Func<T, bool> func) { //遍历数据源的数据 foreach (var item in scoure) { //请求委托完成条件的筛选返回bool bool bResult = func(item); //把通过筛选提交的数据源,返回出去 if (bResult) { yield return item; } } } }
/// <summary> /// 实体模型 /// </summary> public class Student { /// <summary> /// ID /// </summary> public string Id { get; set; } /// <summary> /// 名称 /// </summary> public string Name { get; set; } }
//查询出所有数据 IEnumerable<Student> student = sql.QueryList<Student>(); //定义一个匿名方法,并赋值给委托 Func<Student, bool> func = delegate(Student s) { //自定义代码逻辑,返回bool类型 return s.Id.Equals("1"); }; //传入委托 IEnumerable<Student> list = student.ExtWhere(func); //第二种方法,使用linq语法(自定义逻辑) IEnumerable<Student> list1 = student.ExtWhere(p => p.Id.Equals("1"));
上面就是一个简单但很常见的委托使用场景
从侧面理解一下这段代码,
ExtWhere 是我要做的一件事情,但这件事情里面我需要一个bool类型的返回结果,那么我委托func去帮我获取到这个bool类型的结果
我刚开始的时候,对委托的理解觉得很困难,总感觉晕晕的,但是自己没事多练习练习之后,就会很好理解了
上面的demo很好的解释了使用委托的好处
解耦:抽出自定义逻辑,保留相同的逻辑,使代码分离
最大限度的简化代码:解耦的同时,又减少了代码量(自定义逻辑,可以避免相同逻辑的代码重复)
事件是特殊的委托:
比如MQ的推送也是下面这种
client.MqttMsgPublishReceived -= client_MqttMsgPublishReceived;
client.MqttMsgSubscribed += client_MqttMsgSubscribed;
一个事件可以有很多的侦听者挂接在上面,这些侦听者通过注册自己的事件处理例程来告诉系统说,当这个事件发生的时候请调用我的xxx方法。具体到你这里的代码,MqttMsgPublishReceived 就是一个事件,侦听者想要告诉系统说,当我收到MqttMsgPublishReceived 的时候请你执行我的client_MqttMsgPublishReceived方法。 侦听者怎么样用程序的语言告诉系统呢,就是你这里写好的 += 语句了,那有一天,侦听者可能不感兴趣这个事件了, 那么他可以取消掉自己的注册。 用 -= 咯
+=就是發生新事件的同時通知你;
-=就是發生新事件的同時不通知你;
+=就是发生这个事件的同时要通知你,-=就是发生这个事件不再来通知你了。
Attribute
一,什么是特性
特性也是一种对象,关键字是 Attribute,特殊之处在于其编译时就存在了,也就是在程序运行之前就存在了。
特性(Attribute)是用来 向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。所以要获取某个类的特性,需要通过反射实现
二,是用特性的类必须继承 Attribute
先给段简单代码玩一下
public class student { [ColumnAttribute("Uname")] //这里就是调用了,去掉中括号,实际就是构造函数调用 public string Name { get; set; } } public class ColumnAttribute : Attribute { public string Name { get; private set; } public ColumnAttribute(string name) { this.Name = name; } }