zoukankan      html  css  js  c++  java
  • 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,可以传入参数,没有返回值的委托!

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

     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;
                }
            }
  • 相关阅读:
    我的开发环境配置经验
    C# WINFORM 打包数据库
    C#格式化数值结果表(格式化字符串)
    Excel如何固定表头,任意一行
    下载fiddler证书并设置信任
    fiddler展示serverIP方法
    fiddler抓包参数乱码的解决方法
    fiddler模拟发送get/post请求(也可做简单接口测试)
    Jenkins常用插件
    关于gitignore文件的创建与使用
  • 原文地址:https://www.cnblogs.com/netlock/p/13512369.html
Copyright © 2011-2022 走看看