C#笔记

1、ref与out关键字

不同点:

ref:参数传入方法前,需要先赋值。

out:参数传入方法前,对赋值没有要求,但是方法内必须对参数赋值。

(如果没按要求赋值,编辑器会报错)

相同点:

ref与out标记参数时,方法内改变参数的值,变量的值也发生变化。

protected void Page_Load(object sender, EventArgs e)
    {
        string j="初始值";
        refTest(ref j);
        Console.Write(j); //此时j="改变值",out与ref都会改变 变量 的值。
        test(j);
        Console.Write(j); //此时j="改变值",test方法并没有将j的值改变
    }
    static void refTest(ref string a)
    {
        a = "改变值";
    }

    static void test(string a)
    {
        a = "改变不了";
    }

 2、委托与事件

场景:下一个页面(类)去执行上一个页面(类)的方法。(A页面跳转到B页面,在B页面通过事件的方法,触发A页面的方法运行。)

           用于两个类中的某些方法不方便直接调用。

   A页面:

   B  b=new B();

   b.event +=new  B.method(test);      //事件可以添加多个,里面的方法会依次执行 

   b.main();

  private void test()

  {

      //一些操作,例如girdView重新加载数据

  }

   B页面:

   public delegate void method();

   public event method event1;

   public void main()

   {

       if(event1!=null)

        {

               event1();   //此时会触发A页面页面的test方法。

        }

   }

A页面:

public partial class delegate1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Class1 cl = new Class1();
        cl.shijian += new Class1.weituo(test);   //将test方法委托
        cl.test2(); //执行class1的test2方法

    }
    private void test()
    {
        string a = "ceshi";
    }
}

 B页面:

public class Class1
{
    public delegate void weituo();
    public event weituo shijian;
    public void test2()
    {
        if (shijian != null)
        {
            shijian();  //此时去执行A页面里面test方法。
        }
    }
}

 使用规范:

(1). 委托类型的名称都应该以 EventHandler 结束。

(2). 委托的原型定义:有一个void 返回值,并接受两个输入参数:一个Object 类型,一个EventArgs 类型(或继承自EventArgs)。

(3). 事件的命名为委托去掉 EventHandler 之后剩余的部分。

(4). 继承自 EventArgs 的类型应该以EventArgs 结尾。

3、泛型

    class A<T> where T : class    //T必须是一个class类型

     {

             public T  a;

             public T  method(T t)

             {

                      return t;

             }

     }

       where T : struct | T必须是一个结构类型
  where T : class T必须是一个类(class)类型
  where T : new() | T必须要有一个无参构造函数
  where T : NameOfBaseClass | T必须继承名为NameOfBaseClass的类
  where T : NameOfInterface | T必须实现名为NameOfInterface的接口

4、重写与覆盖

      父类使用virtual关键字修饰的方法(虚方法),子类中可以对父类中的虚方法进行重写(使用override关键字)

      父类中不使用virtual修饰的方法(实方法),子类只能对父类中的实方法进行覆盖(使用new关键字)

      虚方法、实方法都可以被覆盖(new)。

      覆盖与重写的区别:

      重写会改变父类方法的功能,覆盖不会改变父类方法的功能。

      

public class Class1
    {
        public virtual string test1()    //虚方法
        {
            return "重写前";
        }
        public string test2()            //实方法
        {
            return "覆盖前";
        }
    }
    public class Class2 : Class1
    {
        public override string test1()  //使用关键字重写父类的test1方法
        {
            return "重写后";
        }
        public new string test2()//使用new关键字覆盖父类的test1方法
        {
            return "覆盖后";
        }
    }
    public class Class3
    {
        public void test()
        {
            Class1 a = new Class2();
            string z = a.test1();       //此时Z   重写后    Class2将test1重写了。
            string zz = a.test2();      //此时ZZ  覆盖前    
        }
    }

 5、单例模式

       一、使用类行为创建

       单例模式的类中,需要注意的事项:

     (1)类变量  _singleTon需要被private static修饰,用于获取对象的方法(static类型)以及防止类的外部访问。

     (2)获取类对象的方法 getInstance() 需要被public static修饰,外部通过类名加 . 的方式获取对象。

       总结:类中含有两种东西(对象变量_singleTon,获取对象的方法getInstance),其中第一个是private static,后一个public static。

/// <summary>
/// 单例模式
/// </summary>
public class SingleTon
{
    private static SingleTon _singleTon = null;public static SingleTon getInstance()
    {
        if (_singleTon == null)
        {
            _singleTon = new SingleTon();
        }
        return _singleTon;
    }
}

  此时有一个缺点,多线程情况下(两个线程同时获取类对象,就会创建两个对象)

  优化一下(加上同步锁)

/// <summary>
/// 单例模式
/// </summary>
public class SingleTon
{
    private static SingleTon _singleTon = null; 
    private static object SingleTon_lock = new object();  //同步锁
    /// <summary>
    /// 防止类的外部使用new的方式创建该对象
    /// </summary>

    public static SingleTon getInstance()
    {
        lock (SingleTon_lock)
        {
            if (_singleTon == null)
            {
                _singleTon = new SingleTon();
            }
            return _singleTon;
        }
    }
}

     二、使用静态变量创建

public class SingletonThird
    {
        /// <summary>
        /// 静态变量
        /// </summary>
        private static SingletonThird _SingletonThird = new SingletonThird();
        
        public static SingletonThird CreateInstance()
        {
            return _SingletonThird;
        }
    }

是不是觉得很优雅, 利用静态变量去实现单例,  由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次

PS: 但是他的缺点也很明显, 在程序初始化后, 静态对象就被CLR构造了, 哪怕你没用。

   

三、使用静态构造函数创建

public class SingletonSecond
    {
        private static SingletonSecond _SingletonSecond = null;

        static SingletonSecond()
        {
            
            _SingletonSecond = new SingletonSecond();
        }
        
        public static SingletonSecond CreateInstance()
        {
            return _SingletonSecond;
        }
    }

静态构造函数:只能有一个,无参数的,程序无法调用 。

同样是由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次

同静态变量一样, 它会随着程序运行, 就被实例化, 同静态变量一个道理。

6、C#中值类型和引用类型的区别

       (1).    值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。

       (2).     值类型存取速度快,引用类型存取速度慢。

       (3).     值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用

       (4).     值类型继承自System.ValueType,引用类型继承自System.Object

       (5).     栈的内存分配是自动释放;而堆在.NET中会有GC来释放       

       (6).      值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。

       (7). 值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中。注意,堆和堆栈是两个不同的概念,在内存中的存储位置也不相同,堆一般用于存储可变长度的数据,如字符串类型;而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量占用四个字节)。由数据存储的位置可以得知,当把一个值变量赋给另一个值变量时,会在堆栈中保存两个完全相同的值;而把一个引用变量赋给另一个引用变量,则会在堆栈中保存对同一个堆位置的两个引用,即在堆栈保存的是同一个堆的地址。在进行数据操作时,对于值类型,由于每个变量都有自己的值,因此对一个变量的操作不会影响到其它变量;对于引用类型的变量,对一个变量的数据进行操作就是对这个变量在堆中的数据进行操作,如果两个引用类型的变量引用同一个对象,实际含义就是它们在堆栈中保存的堆的地址相同,因此对一个变量的操作就会影响到引用同一个对象的另一个变量。

        int a = 1;
        int b = a;
        a = 3;    //此时b仍然为1
        Class2 cl = new Class2();
        Class2 c2 = cl;
        cl.a = "123456";   //此时引用C2的a也变成了123456

 7、try catch finally 异常

       finally里面的语句,无论程序是否出现异常或者return,都会执行finally里面的语句。

         使用场景(关闭数据库连接,无论是否执行成功,都要关闭了数据库连接)

         使用方法  try与catch(可以有多个,也可以没有,没有时需要加finally,finally只能有一个或者没有)。

try

{

       //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容

} 

catch

{

      //除非try里面执行代码发生了异常,否则这里的代码不会执行

} 

finally

{

     //不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally  

}

 8、索引器

       索引器由             修饰符  返回类型 加 this[参数类型  参数值](public string this[int index,string b]{   })               构成, 索引器的索引值不受类型限制。

        索引器里面需要有get set访问器。

        调用方式可以是:   

        Class cl=new Class();

        cl[0]="对class对象里面a赋值";

        索引器的作用: 可以使得类的实例能够像数组那样使用一样,又称为带参属性        

      (1)索引器与数组的比较:

             索引器的索引值不受类型限制。用来访问数组的索引值一定是整数,而索引器可以是其他类型的索引值。
             索引器允许重载,一个类可以有多个索引器。
             索引器不是一个变量没有直接对应的数据存储地方。索引器有get和set访问器。
             索引器允许类和结构的实例按照与数组相同的方式进行索引,索引器类似与属性,不同之处在于他们的访问器采用参数。被称为有参属性。

      (2)索引器与属性的比较:
            标示方式:属性以名称来标识,索引器以函数签名来标识。
            索引器可以被重载。属性则不可以被重载。
            属性可以为静态的,索引器属于实例成员,不能被声明为static

    public string a { get; set; }
    public string b { get; set; }

    public string this[int index]
    {
        get
        {
            if (index == 0)
            {
                return a;
            }
            else
                return b;
        }
        set
        {
            if (index == 0)
            {
                a = value;
            }
            else
                b = value;
        }
    }

 

posted @ 2019-04-17 15:03  进击的石头--GO!  阅读(195)  评论(0编辑  收藏  举报