C# 泛型 值类型和引用类型 堆和栈 装箱和拆箱 委托和事件 Equals 与== Attribute特性

C# 泛型 值类型和引用类型 堆和栈 装箱和拆箱 委托和事件  Equals 与==  Attribute特性

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;
        }
View Code

2.比较老的 delege 委托,新的升级中有新的关键字 Action 和Func
   2.1,Action,可以传入参数,没有返回值的委托!

C# 泛型 值类型和引用类型 堆和栈 装箱和拆箱 委托和事件  Equals 与==  Attribute特性

 看看函数重载说明就知道了!上代码,一目了然

 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 的区别是可以有返回值

C# 泛型 值类型和引用类型 堆和栈 装箱和拆箱 委托和事件  Equals 与==  Attribute特性

 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方法。那有一天,侦听者可能不感兴趣这个事件了, 那么他可以取消掉自己的注册。 用 -= 咯

+=就是發生新事件的同時通知你;

-=就是發生新事件的同時不通知你;

+=就是发生这个事件的同时要通知你,-=就是发生这个事件不再来通知你了。

 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;
            }
        }