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;
                }
            }
  • 相关阅读:
    POJ 1426 Find The Multiple(数论——中国同余定理)
    POJ 2253 Frogger(Dijkstra变形——最短路径最大权值)
    POJ 3790 最短路径问题(Dijkstra变形——最短路径双重最小权值)
    POJ 3278 Catch That Cow(模板——BFS)
    HDU 1071 The area
    HDU 1213 How Many Tables(模板——并查集)
    POJ 1611 The Suspects
    light oj 1214 Large Division
    POJ 1258 Agri-Net(Prim算法求解MST)
    POJ 2387 Til the Cows Come Home(模板——Dijkstra算法)
  • 原文地址:https://www.cnblogs.com/netlock/p/13512369.html
Copyright © 2011-2022 走看看