.NET static知识入门

    C# 语言中,通过类名调用静态成员,通过指定的实例名调用实例成员。

1. static关键字

    如果有些成员是所有对象共用的,此时可以将成员定义为静态的,当该类被装入内存时,系统会在内存中专门开辟一部分区域来保存这些静态成员。
    static关键字表示类或成员加载到内存中只有一份,而不是有多个实例。当垃圾回收器检测到不再使用该静态成员后,会自动释放内存。
    static 修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。
    static

静态字段常用的用法:
    1. 记录已实例化对象的个数  
    2. 存储必须在所有实例之间共享的值

2. static 静态成员

     static变量在对象被实例化时创建,通过对象进行访问一个类的所有实例的同一C#静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值

static class CompanyEmployee
{
    public static void DoSomething() { /*...*/ }
    public static void DoSomethingElse() { /*...*/  }
     public static cook;
}
  1. 非静态类可以包含静态的方法、字段、属性或事件;
  2. 无论对一个类创建多少个实例,它的静态成员都只有一个副本;
  3. 静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例变量;
  4. 静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;
  5. 虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。因此,可以同对待静态字段一样使用 ClassName.MemberName 表示法来访问 const 字段;
  6. C# 不支持静态局部变量(在方法内部定义静态变量)。
  7. static 声明的静态成员在外部只能通过类名来引用,不能用实例名来引用

2. static 静态类

     声明自定义类时如果加上static关键字,则该类就是静态类
     一般将成员声明为静态,而不是将类声明为静态。如果将类声明为静态,则该类的所有成员都必须声明为静态的。
     静态类可以用作只对输入参数进行操作并且不必获取或设置任何内部实例字段方法集的方便容器。 例如,在 .NET Framework 类库中,静态 System.Math 类包含执行数学运算,而无需存储或检索对 Math 类特定实例唯一的数据的方法。 即,通过指定类名和方法名称来应用类的成员,如下面的示例所示。

double dub = -3.14;  
Console.WriteLine(Math.Abs(dub));  
Console.WriteLine(Math.Floor(dub));  
Console.WriteLine(Math.Round(Math.Abs(dub)));  

// Output:  
// 3.14  
// -4  
// 3

     加载引用静态类的程序时,CLR(公共语言运行库)可以保证在程序首次应用该类前自动加载该类,并初始化该类的字段以及调用静态其静态构造函数。静态类将一直保留在内存中,直至程序结束
程序无法确切指定类加载的时间。 但是,可保证进行加载,以及在程序中首次引用类之前初始化其字段并调用其静态构造函数。 静态构造函数只调用一次,在程序所驻留的应用程序域的生存期内,静态类会保留在内存中。
静态类的主要功能:

1. 只包含静态成员。  
2. 无法进行实例化。  (与私有构造函数机制类似)
3. 会进行密封seal,不能被继承,不能继承自任何类(除了 Object)。  
4. 不能包含实例构造函数,但可包含静态构造函数。 如果在非静态类中包含需要进行重要的初始化的的静态成员,也需要声明为静态构造函数。


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                StaticTest A, B;//定义类的变量
                A = new StaticTest();//创建类的实例A
                B = new StaticTest();//创建类的实例B
                StaticTest.stal = StaticTest.stal + 10;//通过类名给静态变量赋值
                A.a2 = A.a2 + 10;//给实例A的成员A2赋值
                StaticTest.stal = StaticTest.stal + 10;//通过类名给静态变量赋值
                B.a2 = B.a2 + 20;//给实例B的成员赋值
                Console.WriteLine("StaticTest.stal+{0},A.a2={1}", StaticTest.stal, A.a2);//输出静态变量和实例A的成员a2的值
                Console.WriteLine("StaticTest.stal+{0},B.a2={1}", StaticTest.stal, B.a2);//输出静态变量和实例B的成员a2的值
                Console.ReadKey();

            }
        }
        class StaticTest
        {
            public static int stal = 10;//定义静态变量
            public int a2 = 10;//定义实例变量
        }

    }

3. static 静态构造函数

    静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。 将在创建第一个实例或引用任何静态成员之前自动调用静态构造函数。

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    static SimpleClass()
    {
        baseline = DateTime.Now.Ticks;
    }
}

静态构造函数具有以下属性:

  1. 静态构造函数不使用访问修饰符或不具有参数。
  2. 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类。
  3. 不能直接调用静态构造函数。
  4. 用户无法控制在程序中执行静态构造函数的时间。
  5. 静态构造函数只执行一次
  6. 静态构造函数的一种典型用法是在类使用日志文件且将构造函数用于将条目写入到此文件中时使用。
  7. 静态构造函数对于创建非托管代码的包装类也非常有用,这种情况下构造函数可调用 LoadLibrary 方法。
  8. 如果静态构造函数引发异常,运行时将不会再次调用该函数,并且类型在程序运行所在的应用程序域的生存期内将保持未初始化。

引用

http://www.cnblogs.com/linuxnotes/archive/2013/05/24/3096145.html
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
http://blog.csdn.net/xifeijian/article/details/8708252
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
C#程序设计及应用教程 v3 马骏

C#设计模式之单例模式入门

一、引言

    之前用单线程写了一个socket收发数据的小demo,只实现了发送数据后,接收返回的数据,无法进行发送之后,主动接受数据。后来看过单利模式相关的知识点后,就基本上解决了这个问题。

二、单例模式的介绍

    说到单例模式,大家第一反应应该就是——什么是单例模式?,从“单例”字面意思上理解为——一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法罢了(设计模式其实就是帮助我们解决实际开发过程中的方法, 该方法是为了降低对象之间的耦合度,然而解决方法有很多种,所以前人就总结了一些常用的解决方法为书籍,从而把这本书就称为设计模式),下面给出单例模式的一个官方定义:确保一个类只有一个实例,并提供一个全局访问点。为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路:

三、为什么会有单例模式

    看完单例模式的介绍,自然大家都会有这样一个疑问——为什么要有单例模式的?它在什么情况下使用的?从单例模式的定义中我们可以看出——单例模式的使用自然是当我们的系统中某个对象只需要一个实例的情况,例如:操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作等,既然现实生活中有这样的应用场景,自然在软件设计领域必须有这样的解决方案了(因为软件设计也是现实生活中的抽象),所以也就有了单例模式了。

四、剖析单例模式的实现思路

    了解完了一些关于单例模式的基本概念之后,下面就为大家剖析单例模式的实现思路的,因为在我自己学习单例模式的时候,咋一看单例模式的实现代码确实很简单,也很容易看懂,但是我还是觉得它很陌生(这个可能是看的少的,或者自己在写代码中也用的少的缘故),而且心里总会这样一个疑问——为什么前人会这样去实现单例模式的呢?他们是如何思考的呢?后面经过自己的琢磨也就慢慢理清楚单例模式的实现思路了,并且此时也不再觉得单例模式陌生了,下面就分享我的一个剖析过程的:

    我们从单例模式的概念(确保一个类只有一个实例,并提供一个访问它的全局访问点)入手,可以把概念进行拆分为两部分:

         1. 确保一个类只有一个实例;
         2. 提供一个访问它的全局访问点;

    下面通过采用两人对话的方式来帮助大家更快掌握分析思路:

菜鸟:怎样确保一个类只有一个实例了?

老鸟:那就让我帮你分析下,你创建类的实例会想到用什么方式来创建的呢?

新手:用new关键字啊,只要new下就创建了该类的一个实例了,之后就可以使用该类的  
     一些属性和实例方法了            
老鸟:那你想过为什么可以使用new关键字来创建类的实例吗?

菜鸟:这个还有条件的吗?........., 哦,我想起来了,如果类定义私有的构造函数  
     就不能在外界通过new创建实例了(注:有些初学者就会问,有时候我并没有在类
     中定义构造函数为什么也可以使用new来创建对象,那是因为编译器在背后做了手
     脚了,当编译器看到我们类中没有定义构造函数,此时编译器会帮我们生成一个公
     有的无参构造函数)

老鸟:不错,回答的很对,这样你的疑惑就得到解答了啊

菜鸟:那我要在哪里创建类的实例了?

老鸟:你傻啊,当然是在类里面创建了(注:这样定义私有构造函数就是上面的一个思考
     过程的,要创建实例,自然就要有一个变量来保存该实例把,所以就有了私有变量
     的声明,但是实现中是定义静态私有变量,朋友们有没有想过——这里为什么定义为静
     态的呢?对于这个疑问的解释为:每个线程都有自己的线程栈,定义为静态主要是为
     了在多线程确保类有一个实例)            
菜鸟:哦,现在完全明白了,但是我还有另一个疑问——现在类实例创建在类内部,那外界  
     如何获得该的一个实例来使用它了?            
老鸟:这个,你可以定义一个公有方法或者属性来把该类的实例公开出去了(注:这样就
       有了方法的定义了,该方法就是提供方法问类的全局访问点)

    通过上面的分析,相信大家也就很容易写出单例模式的实现代码了,下面就看看具体的实现代码(看完之后你会惊讶道:真是这样的!):

/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
    // 定义一个静态变量来保存类的实例
    private static Singleton uniqueInstance;

    // 定义私有构造函数,使外界不能创建该类实例
    private Singleton()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static Singleton GetInstance()
    {
        // 如果类的实例不存在则创建,否则直接返回
        if (uniqueInstance == null)
        {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}  

    上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了(对于线程同步大家也可以参考我线程同步的文章),具体的解决多线程的代码如下:

/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
    // 定义一个静态变量来保存类的实例
    private static Singleton uniqueInstance;

    // 定义一个标识确保线程同步
    private static readonly object locker = new object();

    // 定义私有构造函数,使外界不能创建该类实例
    private Singleton()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static Singleton GetInstance()
    {
        // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
        // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
        // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
        lock (locker)
        {
            // 如果类的实例不存在则创建,否则直接返回
            if (uniqueInstance == null)
            {
                uniqueInstance = new Singleton();
            }
        }

        return uniqueInstance;
    }
}

    上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”,下面具体看看实现代码的:

/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
    // 定义一个静态变量来保存类的实例
    private static Singleton uniqueInstance;

    // 定义一个标识确保线程同步
    private static readonly object locker = new object();

    // 定义私有构造函数,使外界不能创建该类实例
    private Singleton()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static Singleton GetInstance()
    {
        // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
        // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
        // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
        // 双重锁定只需要一句判断就可以了
        if (uniqueInstance == null)
        {
            lock (locker)
            {
                // 如果类的实例不存在则创建,否则直接返回
                if (uniqueInstance == null)
                {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

参考

http://www.cnblogs.com/zhili/p/DesignPatternSummery.html (以后直接看)
http://www.cnblogs.com/zhili/p/SingletonPatterm.html

C#内存管理入门

  一直以来对内存泄漏、内存溢出不太关心,而且认为 C# 的垃圾回收机制已经很完善了,所以在写代码时对之并不关心。后来被问及相关知识,就嘎屁了。
  C# 语言的一个好处是不需要考虑对内存的分配和释放。我们只需要创建对象,然后通过垃圾回收机制(GC)来处理这些对象,即:应用程序会自动清理当不再使用的对象。

  • 垃圾收集器的工作就是寻找那些不再被应用程序需要的对象,当它们不会再被访问或引用的时候清除它们。(一定要注意是在不会再被访问或引用的时候才清除它们)
  • 一个对象只有当它不再被引用的时候才会被当作是无用的

内存溢出

    指程序在运行过程中,程序对内存的需求超过计算机分配给程序的内存,从而造成“out of memory”之类的错误,使程序崩溃死亡

内存泄漏

    指由于疏忽或错误造成程序不能及时释放不再使用的内存,是应用程序分配某段内存后,由于某种原因而失去对该段内存的控制

托管资源

    托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收

非托管资源

    非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等。这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。

对象创建的过程

A a = new A();

创建A的对象并对其进行初始化。
  A:类;
  new A():创建A的对象并对其初始化;
  a:引用,指向new A()这个对象的引用。
  注意:a不是A的对象,new A()才是A的对象。

  1. 为字段分配内存并进行初始化;
  2. 如果类是第一次加载(即系统中从未创建类的其它对象,或者曾经创建但因不再有引用而被 GC 全部回收),则 Copy 其实例方法至方法表;
  3. 为类中需要直接赋值的字段赋值;
  4. 执行构造函数。

栈和托管堆

  • 对象的分配是在托管堆中执行的,
  • 栈只是存储对对象的引用。
  • 当栈在执行过程中,如果释放了对对象的引用,垃圾回收会开始回收引用计数为0的对象占用的内存。
        托管堆是以应用程序域为依托的,即每一个应用程序域有一个托管堆,每一个托管堆也只属于一个应用程序域,且托管堆是一块连续的内存,其中的对象也是紧密排列的。相对于C++中的非连续内存堆来说,托管堆的内存分配效率要高。托管堆维护了一个指针,指向当前已使用内存的末尾,当需要分配内存的时候,只需要指针向后移动指定数量的位置即可。而且托管堆通过应用程序域实现了应用程序之间内存的隔离,即不同的应用程序域之间在正常情况下是不能相互访问各自的托管堆的。

##

解决方案

    .net自带内存回收机制。说的更直接一些,就是我们使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。而在该对象离开他的生存期后自然就被释放了。我们并没有参与其中的操作。但是往往这样,还是不够。因为.NET的GC机制有这样两个问题:
首先,GC并不是能释放所有的资源。它不能自动释放非托管资源。
其次,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。
因此我们还可以了解以下方式。

1、引用计数

  引用计数是指,针对每一个对象,保存一个对该对象的引用计数,该对象的引用增加,则相应的引用计数增加。如果对象的引用计数为零,则回收该对象。

    优点:引用计数最大的优点就是容易实现。二是成本小,基本上引用计数为0的时候垃圾会被立即回收,而其他方法难以预测对象的生命周期,垃圾存在的时间都会比这个方法高。另,这种垃圾回收方式产生的中断时间最短。

    缺点:最著名的缺点就是如果对象中存在循环引用,就无法被回收。例如,下面三个对象互相引用,但是不存在从根(Root)指向的引用,所以已经是垃圾了。但是引用计数不为0.

这里写图片描述

2、标记清除法(mark-weep)

    C#中采用的是标记法回收内存,全部对象都要标记,并且只标记一次就不再标记。判断一个对象是不是垃圾取决于是否有引用,而是取决是是否被root引用。

3、手动进行垃圾回收(GC(Garbagecollection)

如上文所说,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。或使用using语句可以简化资源管理。

##

  1.在声明对象和定义变量的时候最好是在定义的时候 给一个NUll,如果 到本行代码以后不会再使用这个对象了, 请把它设置为Null
private DataTable _dt_two = new DataTable(); 
private DataTable _dt_officeInfo = new DataTable();  

  这样做很明显是不合理的,因为你也不知道在使用的过程 中到低会不会加载数据,当然这样确定 不会再出现未对对象引用到对象实例这样的错误 了,当然也说明了一个问题,这样做是不合理的,正确的应该是这样

// 全局声明
private DataTable _dt_two = null;
private DataTable _dt_officeInfo = null;

// 使用时实例化
_dt_two =getDataTable();
_dt_officeInfo =getDataTable();

// 不再使用数据
_dt_two =null;//
_dt_officeInfo =null

GC.Collect();
GC.WaitForPendingFinalizers();

  而当使用的时候 再给其真正的值。
  其实比较常见的有一点:
  A的方法挂了B的事件, 而在A需要被释放的时候却没有摘掉事件。
  那么A就不会被回收。
  因为A在挂B的时间的时候 ,在B里面保存了一个A的引用。
  其他的情况下很少有不能回收的。
  不能回收, 说明这东西还有用。 不是垃圾。

##

  2.静态变量、集合

    对于集合,我们在使用完成之后,需要即时的clear,尤其是将一些方法中的临时变量添加到集合当中之后,会导致集合膨胀,并使得其中的内存泄露。
    对于静态字段,我们应该尽量减少其可见的域,因为静态字段在整个程序运行期间都不会被释放,减少其可见域就减少了其内存泄露的可能性,注意,不到万不得以,千万不要声明静态的集合,就是使用了,那也一定要小心再小心。静态集合很容易造成内存泄露。

##

  4.非托管资源回收机制 例子

    析构函数是一种用于实现销毁类实例所操作的成员的函数。析构函数不能带参数,也不能包含访问修饰符。另外,析构函数是自动调用有的,不能显示调用。由于GC相对来说已经很完善,所以只在某些特殊的高级开发中才需要声明的。
    如果声明析构函数,开发人员可通过System.GC的静态方法在一定程度上控制垃圾回收行为。该类可用于请求执行一次回收操作,并自动用于请求是否该执行析构函数

 A a=null;
try
{
    a=new A();
}
finally
{
    if(a!=null)
    {
        a.Dispose();
    }
}

    即使处理过程中出现异常,可以确保在a上调用 Dispose();方法,总是释放a使用的任意资源

##

Public class BaseResource:IDisposable
{
    PrivateIntPtr handle; // 句柄,属于非托管资源
    PrivateComponet comp; // 组件,托管资源
    Privateboo isDisposed = false; // 是否已释放资源的标志

    PublicBaseResource
    {
    }

    //实现接口方法
    //由类的使用者,在外部显示调用,释放类资源
    Publicvoid Dispose()
    {
        Dispose(true);// 释放托管和非托管资源

        //将对象从垃圾回收器链表中移除,
        // 从而在垃圾回收器工作时,只释放托管资源,而不执行此对象的析构函数
        GC.SuppressFinalize(this);
    }

    //由垃圾回收器调用,释放非托管资源
    ~BaseResource()
    {
        Dispose(false);// 释放非托管资源
    }

    //参数为true表示释放所有资源,只能由使用者调用
    //参数为false表示释放非托管资源,只能由垃圾回收器自动调用
    //如果子类有自己的非托管资源,可以重载这个函数,添加自己的非托管资源的释放
    //但是要记住,重载此函数必须保证调用基类的版本,以保证基类的资源正常释放
    Protectedvirtual void Dispose(bool disposing)
    {
        If(!this.disposed)// 如果资源未释放 这个判断主要用了防止对象被多次释放
        {
            If(disposing)
            {
                Comp.Dispose();// 释放托管资源
            }
            closeHandle(handle);// 释放非托管资源
            handle= IntPtr.Zero;
        }
        this.disposed= true; // 标识此对象已释放
    }
}

##

class A
{
    ~A(){Console.WriteLine("这是析构函数")}
}
class Test
{
    static void Main()
    {
        A a=new A();
        a=null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

##

   5.using() { / statement / }

    using语句的后面有一对圆括号,用于应用变量的声明和实例化,该语句使得变量放在随后的语句块中。另外,变量在超出作用域后,即使出现异常,也会自动调用Dispose()方法.
    对于某些类,使用Close()方法要比Dispose()方法更具有逻辑性。例如,在处理文件或数据库时就是这样。在这些情况下,常常实现IDispose()接口,在实现一个独立的Close()方法,Close()方法只调用Dispose()方法,还可以避免额外的代码缩进。

参考

http://blog.csdn.net/fhzh520/article/details/7872305
http://www.cnblogs.com/sufei/archive/2010/01/18/1650257.html
http://blog.jobbole.com/89064/
《C#程序设计及应用教程》 V3 马骏
《C#高级教程》 v7

JavaScript变量提升、函数提升

查到一篇写的简单易懂,索性就直接用了,另做了部分补充

前言
因为我在写这文章的时候,百度里找资料,找到了园友的一篇文章,写的很好,可是我写了又不想放弃,所以就在里面拿了很多东西过来!~~

[翻译]JavaScript Scoping and Hoisting

希望得到大家谅解。

因为这个问题很是经典,而且容易出错,所以在介绍一次。哈哈。莫怪哦。

一。案发现场
我们先看一段很简单的代码:

var v='Hello World';
alert(v);

这个没有疑问吧,弹出“Hello World”。OK,我们继续。

我们在看一段Code:

var v='Hello World';
(function(){
    alert(v);
})()

经过运行之后,我们发现,还是和我们预期的一样,弹出了“Hello World”。

好了,有意思的来了。接着在看一段下面的代码:

var v='Hello World';
(function(){
    alert(v);
    var v='I love you';
})()

如果这个是一个面试题,面试官问你这个结果是多少?你怎么回答?

我们先看结果吧!

结果是 undefined?和你上面自己想的一样吗?

好吧,我就不故弄玄虚了。其实,这里面隐藏了一个陷阱—–JavaScript中的变量提升(Hoisting);

二。深度剖析
现在我来解释下提升是什么意思?顾名思义,就是把下面的东西提到上面。在JS中,就是把定义在后面的东东(变量或函数)提升到前面中定义。

在解释提升之前,我们先来看一下js中的作用域(scoping)问题。

对于JavaScript新手来说scoping是最令人困惑的部分之一。事实上,不仅仅是新手,我遇到或很多有经验的JavaScript程序员也不能完全理解scoping。JavaScript的scoping如此复杂的原因是它看上去非常像C系语言的成员。请看下面的C程序:

复制代码

#include <stdio.h>
    int main() {
    int x = 1;
    printf("%d, ", x); // 1
    if (1) {
        int x = 2;
        printf("%d, ", x); // 2
     }
    printf("%d\n", x); // 1
}

复制代码
这段程序的输出是1,2,1。这是因为在C系语言有块级作用域(block-level scope),当进入到一个块时,就像if语句,在这个块级作用域中会声明新的变量,这些变量不会影响到外部作用域。但是JavaScript却不是这样。在Firebug中试试下面的代码:

复制代码

 var x = 1;
    console.log(x); // 1
 if (true) {
   var x = 2;
   console.log(x); //2
}
 console.log(x);// 2

复制代码
在这段代码中,Firebug显示1,2,2。这是因为JavaScript是函数级作用域(function-level scope)。这和C系语言是完全不同的。块,就像if语句,并不会创建一个新的作用域。只有函数才会创建新的作用域。

对于大部分熟悉C,C++,C#或是Java的程序员来说,这是意料之外并且不被待见的。幸运的是,因为JavaScript函数的灵活性,对于这个问题我们有一个解决方案。如果你必须在函数中创建一个临时的作用域,请像下面这样做:

复制代码

function foo() {
    var x = 1;
    if (x) {
        (function () {
            var x = 2;
            // some other code
        }());
    }
    // x is still 1.
}

复制代码
这种方面确实非常灵活,它使用在任何需要创建一个临时作用域的地方,不仅仅是某个块中。但是,我强烈建议你花点时间好好理解下JavaScript scoping。它实在是非常强力,而且它也是我最喜欢的语言特性之一。如果你很好的理解了scoping,理解hoisting将会更加容易。

2.1变量提升
变量提升,很简单,就是把变量提升提到函数的top的地方。我么需要说明的是,变量提升 只是提升变量的声明,并不会把赋值也提升上来。

比如:

我们定义三个变量:

(function(){
    var a='One';
    var b='Two';
    var c='Three';
})()

实际上它是这样子的:

复制代码

(function(){
    var a,b,c;
    a='One';
    b='Two';
    c='Three';

})()
复制代码
这个时候就把变量提升了呀。

好,我们现在回到第一段code里面。为什么会报错呢?其实,根据我么根据上面变量提升原件以及js的作用域(块级作用域)的分析,得知 上面代码真正变成如下:

复制代码

var v='Hello World';
(function(){
    var v;
    alert(v);
    v='I love you';
})()

复制代码
所以,才会提示说“undefined”。

从这里,我们也学习到,我们在写js code 的时候,我么需要把变量放在函数级作用域的顶端,比如我在上面所举的例子:var a,b,c;。以防止出现意外。

2.2 函数提升
函数提升是把整个函数都提到前面去。

在我们写js code 的时候,我们有2中写法,一种是函数表达式,另外一种是函数声明方式。我们需要重点注意的是,只有函数声明形式才能被提升。

函数声明方式提升【成功】

复制代码

function myTest(){
    foo();
    function foo(){
        alert("我来自 foo");
    }
}
myTest();

复制代码
函数表达式方式提升【失败】

复制代码

function myTest(){
    foo();
   var foo =function foo(){
        alert("我来自 foo");
    }
}
myTest();

复制代码
结果如下:

左边报错了。没骗你。

应该到这里基本都可以弄懂了。~

Declarations, Names, and Hoisting

在JavaScript中,一个作用域(scope)中的名称(name)有以下四种:

1. 语言自身定义(Language-defined): 所有的作用域默认都会包含this和arguments。

2. 函数形参(Formal parameters): 函数有名字的形参会进入到函数体的作用域中。

3. 函数声明(Function decalrations): 通过function foo() {}的形式。

4. 变量声明(Variable declarations): 通过var foo;的形式。

函数声明和变量声明总是被JavaScript解释器隐式地提升(hoist)到包含他们的作用域的最顶端。很明显的,语言自身定义和函数形参已经处于作用域顶端。这就像下面的代码:

1:  function foo() {
2:      bar();
3:      var x = 1;
4:  }

实际上被解释成像下面那样:

1:  function foo() {
2:      var x;
3:      bar();
4:      x = 1;
5:  }

结果是不管声明是否被执行都没有影响。下面的两段代码是等价的:

 1:  function foo() {
 2:      if (false) {
 3:          var x = 1;
 4:      }
 5:      return;
 6:      var y = 1;
 7:  }
 8:  function foo() {
 9:      var x, y;
10:      if (false) {
11:          x = 1;
12:      }
13:      return;
14:      y = 1;
15:  }

注意到声明的赋值部分并没有被提升(hoist)。只有声明的名称被提升了。这和函数声明不同,函数声明中,整个函数体也都会被提升。但是请记住,声明一个函数一般来说有两种方式。考虑下面的JavaScript代码:

 1:  function test() {
 2:      foo(); // TypeError "foo is not a function"
 3:      bar(); // "this will run!"
 4:      var foo = function () { // 函数表达式被赋值给变量'foo'
 5:          alert("this won't run!");
 6:      }
 7:      function bar() { // 名为'bar'的函数声明
 8:          alert("this will run!");
 9:      }
10:  }
11:  test();

在这里,只有函数声明的方式会连函数体一起提升,而函数表达式中只会提升名称,函数体只有在执行到赋值语句时才会被赋值。

以上就包括了所有关于提升(hoisting)的基础,看起来并没有那么复杂或是令人困惑对吧。但是,这是JavaScript,在某些特殊情况下,总会有那么一点复杂。

Name Resolution Order

需要记住的最最重要的特例就是名称解析顺序(name resolution order)。记住一个名称进入一个作用域一共有四种方式。我上面列出的顺序就是他们解析的顺序。总的来说,如果一个名称已经被定义了,他绝不会被另一个拥有不用属性的同名名称覆盖。这就意味着,函数声明比变量声明具有更高的优先级。但是这却不意味着对这个名称的赋值无效,仅仅是声明的部分会被忽略而已。但是有下面几个例外:

内置的名称arguments的行为有些怪异。他似乎是在形参之后,函数声明之前被声明。这就意味着名为arguments的形参会比内置的arguments具有更高的优先级,即使这个形参是undefined。这是一个不好的特性,不要使用arguments作为形参。
任何地方试图使用this作为一个标识都会引起语法错误,这是一个好的特性。
如果有多个同名形参,那位于列表最后的形参拥有最高的优先级,即使它是undefined。

Name Function Expressions

你可以在函数表达式中给函数定义名称,就像函数声明的语句一样。但这并不会使它成为一个函数声明,并且这个名称也不会被引入到作用域中,而且,函数体也不会被提升(hoist)。这里有一些代码可以说明我说的是什么意思:

 1:  foo(); // TypeError "foo is not a function"
 2:  bar(); // valid
 3:  baz(); // TypeError "baz is not a function"
 4:  spam(); // ReferenceError "spam is not defined"
 5:   
 6:  var foo = function () {}; // 匿名函数表达式('foo'被提升)
 7:  function bar() {}; // 函数声明('bar'和函数体被提升)
 8:  var baz = function spam() {}; // 命名函数表达式(只有'baz'被提升)
 9:   
10:  foo(); // valid
11:  bar(); // valid
12:  baz(); // valid
13:  spam(); // ReferenceError "spam is not defined"

How to Code With This Knowledge

现在你明白了作用域和提升,那么这对编写JavaScript代码意味着什么呢?最重要的一条是声明变量时总是使用var语句。我强烈的建议你在每个作用域中都只在最顶端使用一个var。如果你强制自己这么做,你永远也不会被提升相关的问题困扰。尽管这么做会使的跟踪当前作用域实际声明了哪些变量变得更加困难。我建议在JSLint使用onevar选项。如果你做了所有前面的建议,你的代码看起来会是下面这样:

1:  /*jslint onevar: true [...] */
2:  function foo(a, b, c) {
3:      var x = 1,
4:          bar,
5:          baz = "something";
6:  }

多使用let

第一次接触let关键字,有一个要非常非常要注意的概念就是”JavaScript 严格模式”,比如下述的代码运行就会报错:

let hello = 'hello world.';
console.log(hello);

错误信息如下:

let hello = 'hello world.';
^^^

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
    ...

解决方法就是,在文件头添加”javascript 严格模式”声明:

'use strict';

let hello = 'hello world.';
console.log(hello);

4
更多更详细的关于”javascript 严格模式”说明,请参考阮一峰的博客
http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html

let和var关键字的异同

声明后未赋值,表现相同

'use strict';

(function() {
  var varTest;
  let letTest;
  console.log(varTest); //输出undefined
  console.log(letTest); //输出undefined
}());

使用未声明的变量,表现不同:

(function() {
  console.log(varTest); //输出undefined(注意要注释掉下面一行才能运行)
  console.log(letTest); //直接报错:ReferenceError: letTest is not defined

  var varTest = 'test var OK.';
  let letTest = 'test let OK.';
}());

重复声明同一个变量时,表现不同:

'use strict';

(function() {
  var varTest = 'test var OK.';
  let letTest = 'test let OK.';

  var varTest = 'varTest changed.';
  let letTest = 'letTest changed.'; //直接报错:SyntaxError: Identifier 'letTest' has already been declared

  console.log(varTest); //输出varTest changed.(注意要注释掉上面letTest变量的重复声明才能运行)
  console.log(letTest);
}());

变量作用范围,表现不同

'use strict';

(function() {
  var varTest = 'test var OK.';
  let letTest = 'test let OK.';

  {
    var varTest = 'varTest changed.';
    let letTest = 'letTest changed.';
  }

  console.log(varTest); //输出"varTest changed.",内部"{}"中声明的varTest变量覆盖外部的letTest声明
  console.log(letTest); //输出"test let OK.",内部"{}"中声明的letTest和外部的letTest不是同一个变量
}());

参考:
http://www.cnblogs.com/damonlan/archive/2012/07/01/2553425.html
http://www.cnblogs.com/betarabbit/archive/2012/01/28/2330446.html
http://blog.csdn.net/nfer_zhuang/article/details/48781671

.NET 向Amazon S3批量上传数据

.NET 从TELERIK批量获取数据

我在上传数据时使用PutObjectRequest类,其中的Key属性有严格限制,稍有不慎就会进坑,而创建一个假文件(48B或48K左右)

 

put.Key=文件夹名+"/"+ 文件名

文件名=本地文件名`</pre>
</div>
思路:遍历文件夹,读取所有文件夹、文件夹内文件,使用Task循环上传

&nbsp;
<div>
<pre>`class UploadFilesInAll
{
    /// &lt;summary&gt;
    /// 访问密钥
    /// &lt;/summary&gt;
    private static String accessKeyID = "访问密钥id";
    /// &lt;summary&gt;
    /// 密钥
    /// &lt;/summary&gt;
    private static String secretKey = "私有访问密钥iD";
    /// &lt;summary&gt;
    /// 存储桶的名字
    /// &lt;/summary&gt;
    private static String bucketName = "存储桶名字";
    /// &lt;summary&gt;
    /// 存放上传失败的文件的信息
    /// &lt;/summary&gt;
    private static List&lt;string&gt; errorList = new List&lt;string&gt;();
    /// &lt;summary&gt;
    /// 存放获取到的路径
    /// &lt;/summary&gt;
    static List&lt;string&gt; fileList = new List&lt;string&gt;();
    /// &lt;summary&gt;
    /// 成功个数
    /// &lt;/summary&gt;
    static int successMum = 0;
    /// &lt;summary&gt;
    /// 失败个数
    /// &lt;/summary&gt;
    static int failedNum = 0;
    /// &lt;summary&gt;
    /// file个数
    /// &lt;/summary&gt;
    static int fileCount = 0;
    /// &lt;summary&gt;
    /// folder个数
    /// &lt;/summary&gt;
    static int folderCount = 0;

    /// &lt;summary&gt;
    /// 批量上传文件的主体部分
    /// &lt;/summary&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    public static string UploadAllFiles()
    {
        #region 删除日志文件
        string filePath = AppDomain.CurrentDomain.BaseDirectory + "Log.txt";
        if (System.IO.File.Exists(filePath))
        {
            System.IO.File.Delete(filePath);
        } 
        #endregion

        string localPath = "";
        AWSCredentials credentials;
        credentials = new BasicAWSCredentials(accessKeyID.Trim(), secretKey.Trim());
        AmazonS3Client s3Client = new AmazonS3Client(accessKeyID.Trim(), secretKey.Trim(), Amazon.RegionEndpoint.USEast1);
        Console.WriteLine("验证成功");
        Console.WriteLine("检查存储桶是否存在");
        if (!Program.CheckBucketExists(s3Client, bucketName))
        {
            Console.WriteLine("正在创建数据桶........");
            s3Client.PutBucket(bucketName);
            Console.WriteLine("数据桶创建成功");
        }

        Dictionary&lt;string, List&lt;string&gt;&gt; fileList = UploadFilesInAll.GetAllFiles(@"E:\telerikFile");
        WriteLog("共有" + fileCount + "个文件");
        // 整理文件的路径,逐个上传
        foreach (var item in fileList)
        {
            string key = item.Key.Substring(item.Key.LastIndexOf('\\'));
            key = key.Substring(key.LastIndexOf('\\') + 1);
            Console.WriteLine(key);
            foreach (var item_ in item.Value)
            {
                localPath = item_;
                string path = item_.Substring(item_.LastIndexOf('\\')+1);
                Task.Run(()=&gt; UploadFilesInAll.upload(s3Client, key+"/"+ path, item_));
            }
        }
        try
        {
            Console.WriteLine("准备上传");
            return "success";
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());

            return "failed";
        }

    }

    /// &lt;summary&gt;
    /// 文件上传部分
    /// &lt;/summary&gt;
    /// &lt;param name="s3Client"&gt;Amazon S3服务接口&lt;/param&gt;
    /// &lt;param name="key"&gt;对象键(文件上传后存储的文件夹/文件)&lt;/param&gt;
    /// &lt;param name="localFile"&gt;待上传数据存放路径&lt;/param&gt;
    private static void upload(AmazonS3Client s3Client,string key,string localFile)
    {
        Object objLock = new Object();
        lock(objLock)
        {
            try
            {
                // 配置上传信息
                string localPath = localFile;
                PutObjectRequest obj = new PutObjectRequest();
                var fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read);
                obj.InputStream = fileStream;
                obj.BucketName = bucketName;
                obj.Key = key;
                obj.CannedACL = S3CannedACL.PublicRead;
                Console.WriteLine("正在上传" + localPath);
                // 默认添加public权限  
                s3Client.PutObject(obj);
                Console.WriteLine("上传成功第"+ (successMum+1) +"个文件" );
                WriteLog("上传成功第" + (successMum+1) + "个文件" + localPath);
                successMum++;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Console.WriteLine("上传失败"+ (failedNum+1) + "个文件" + localFile);
                errorList.Add(localFile);
                //WriteLog(" 失败" + filedNum++ + "个");
                failedNum++;
                WriteLog("上传失败" + localFile);
                // return "failed";
            }
        }
        if(successMum + 1== fileCount)
        {
            Console.WriteLine("文件已经全部下载,按任意键退出");
            WriteLog("文件已经全部下载");
            Console.ReadKey();
        }

        if(failedNum &gt;=1)
        {
            WriteLog("当前失败率=" + failedNum * 1.00 / fileCount);
            ShowError(s3Client);
        }
        else
        {
            //WriteLog("当前失败率=" + failedNum * 1.00 / fileCount);
        }

    }

    /// &lt;summary&gt;
    /// 再次上传主体部分
    /// &lt;/summary&gt;
    /// &lt;param name="s3Client"&gt;Amazon S3服务主体部分&lt;/param&gt;
    /// &lt;param name="failedPath"&gt;&lt;/param&gt;
    private static void uploadAgain(AmazonS3Client s3Client,string failedPath)
    {

        Object objLock = new Object();
        lock (objLock)
        {
            try
            {
                string key = failedPath.Substring(failedPath.LastIndexOf('\\'));
                key = key.Substring(key.LastIndexOf('\\') + 1);

                string localPath = failedPath;
                PutObjectRequest obj = new PutObjectRequest();
                var fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read);
                obj.InputStream = fileStream;
                obj.BucketName = bucketName;
                obj.Key = key;
                obj.CannedACL = S3CannedACL.PublicRead;
                Console.WriteLine("正在上传" + localPath);
                // 默认添加public权限  
                s3Client.PutObject(obj);
                Console.WriteLine("上传成功" + localPath);
                successMum++;
                errorList.Remove(localPath);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Console.WriteLine("上传失败" + failedPath);
                errorList.Add(failedPath);
                failedNum++;
                WriteLog(failedPath);
            }
            if ((failedNum * 1.00 / fileCount) &gt; 0.00)
            {
                WriteLog("成功下载" + successMum + "个文件,失败" + failedNum + "个文件");
                ShowError(s3Client);
            }
        }
    }

    /// &lt;summary&gt;
    /// 整理错误信息,准备重新下载之前下载失败的文件
    /// &lt;/summary&gt;
    /// &lt;param name="s3Client"&gt;AmazonS3服务实例&lt;/param&gt;
    private static void ShowError(AmazonS3Client s3Client)
    {
        Object objLock = new Object();
        lock (objLock)
        {
            int i = 0;
            string path = "";
            Console.WriteLine("当前失败率=" + failedNum * 1.00 / fileCount);
            WriteLog("当前失败率=" + failedNum * 1.00 / fileCount);
            try
            {
                successMum = 0;
                failedNum = 0;
                foreach (var item in errorList)
                {
                    i++;
                    path = item;
                    Console.WriteLine(item);
                    Task.Run(() =&gt; uploadAgain(s3Client, item));
                }
            }
            catch
            {
                WriteLog("第"+i+"个失败:" + path);
                Console.WriteLine("执行错误");
            }

        }
    }     

    /// &lt;summary&gt;
    /// 获取所有文件的路径
    /// &lt;/summary&gt;
    /// &lt;param name="path"&gt;&lt;/param&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    public static Dictionary&lt;string, List&lt;string&gt;&gt; GetAllFiles(string path)
     {
        Dictionary&lt;string, List&lt;string&gt;&gt; fileTree = new Dictionary&lt;string, List&lt;string&gt;&gt;();
        List&lt;string&gt; list;
        DirectoryInfo[] folder = new DirectoryInfo(@"E:\telerikFile").GetDirectories();
        for (int i = 0; i &lt; folder.Length; i++)
        {
            folderCount++;
            list = list = new List&lt;string&gt;();
            for (int j = 0; j &lt; folder[i].GetFiles().Length; j++)
            {
                fileCount++;
                list.Add(folder[i].GetFiles()[j].FullName.ToString());
            }
            fileTree.Add(folder[i].FullName, list);

            // list.Clear();
        }
        WriteLog("共有"+ folderCount +"个文件夹,"+ fileCount+"个文件");
        return fileTree;
        //foreach (var dicName in list)
        //{
        //    Console.WriteLine(dicName);
        //    Console.WriteLine(dicName.Count());
        //}
    }

    /// &lt;summary&gt;
    /// 获取指定的根目录的所有子目录名称
    /// &lt;/summary&gt;
    /// &lt;param name="path"&gt;指定的根目录&lt;/param&gt;
    /// &lt;returns&gt;子目录名称集合&lt;/returns&gt;
    public static List&lt;string&gt; getPath(string path)
    {
        DirectoryInfo dir = new DirectoryInfo(path);
        FileInfo[] fil = dir.GetFiles();
        DirectoryInfo[] dii = dir.GetDirectories();
        foreach (FileInfo f in fil)
        {
            fileList.Add(f.FullName);//添加文件的路径到列表
        }
        //获取子文件夹内的文件列表,递归遍历
        foreach (DirectoryInfo d in dii)
        {
            getPath(d.FullName);
            fileList.Add(d.FullName);//添加文件夹的路径到列表
        }
        return fileList;
    }

    /// &lt;summary&gt;
    /// 写入文件,做记录测试
    /// &lt;/summary&gt;
    /// &lt;param name="str"&gt;&lt;/param&gt;
    private static void WriteLog(string str)
    {
        Task.Run(() =&gt;
        {
            try
            {
                string filePath = AppDomain.CurrentDomain.BaseDirectory + "Log.txt";
                StreamWriter sw = null;
                if (!System.IO.File.Exists(filePath))
                {
                    sw = System.IO.File.CreateText(filePath);
                }
                else
                {
                    sw = System.IO.File.AppendText(filePath);
                }
                sw.Write(str + Environment.NewLine);//+ DateTime.Now.ToString()
                sw.Close();
            }
            catch (Exception ex)
            {
                ex.ToString();
            }
        });
    }
}


 

JavaScript原型、原型链

  • JS中的对象都从原型继承属性
  • 所有通过对象直接量创建的对象,都具有同一原型对象object.prototype
  • 通过Object.prototype获得对原型对象的引用
  • 通过[new]和[构造函数]和[{}]创建的对象的原型===[构造函数]的prototype属性的值
  • object.prototype无原型对象
    以下为Date.prototype的部分返回值

由此,可理解为:对象自带的一组方法、属性,并且可以通过object.prototype进行自定义原型

原型使用方式1:

在使用原型之前,我们需要先将代码做一下小修改:


        var Calculator = function (decimalDigits, tax) {
this.decimalDigits = decimalDigits;
this.tax = tax;
};


然后,通过给Calculator对象的prototype属性赋值对象字面量来设定Calculator对象的原型。

        Calculator.prototype = {
add: function (x, y) {
return x + y;
},

subtract: function (x, y) {
return x - y;
}
};
//alert((new Calculator()).add(1, 3));


 

上方创建一个Calculator对象,在内部实现setter赋值。而后通过prototype属性来设定对象的原型

这样,我们就可以new Calculator对象以后,就可以调用add方法来计算结果了。var a=new Calculator();a.add(1,2);//3

 

每一个函数、对象都有3大属性

## prototype —(原型是对象,实例继承对象的属性) 实例原型 构造函数指向原型

每一个JavaScript对象(null除外)在创建的时候就会与关联另一个对象,这个对象就是我们所说的原型每一个对象都会从原型”继承”属性

函数的prototype属性指向一个对象 这个对象,就点用该构造函数而创建的实例的原型
* 对象===a的原型


function Person() { }
// 虽然写在注释里,但是你要注意:
// prototype是函数才会有的属性
Person.prototype.name = ‘Kevin’;
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin</pre> </div> &nbsp; ![构造函数和实例原型的关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype1.png)![实例与实例原型的关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype2.png) <div> ## __proto__ ---对象指向原型 </div> 对象的属性,指向该对象的原型,见上方右图 <div> <pre>function Person() { }
var person = new Person();
console.log(person.proto === Person.prototype);
// true`
## constructor   原型指向构造函数

原型的属性

一个构造函数可以生成多个实例==》无法通过原型指向实例

![实例原型与构造函数的关系图](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype3.png)

## 实例与原型
  • 实例有自己的属性
  • 实例“继承”原型的属性
  • 实例不覆盖、不重写原型的属性
    当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。


    `function Person() {

    }

    Person.prototype.name = ‘Kevin’;

    var person = new Person();

    person.name = ‘Daisy’;
    console.log(person.name) // Daisy

    delete person.name;
    console.log(person.name) // Kevin`

    在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。

    但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。

    原型的原型 — 原型是对象

  • 原型是对象

  • 原型对象是通过 Object 构造函数生成的

    var obj = new Object();
    obj.name = 'Kevin'
    console.log(obj.name) // Kevin


    Object.prototype 的原型是null ,没有原型

    `console.log(Object.prototype.proto === null) // true


原型链示意图

 

原型链

上图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。

 

参考:http://www.cnblogs.com/tomxu/archive/2012/01/05/2305453.html#undefined

https://github.com/mqyqingfeng/Blog/issues/2

.Net 从Telerik批量获取数据

之前的学长写的一个APP部署在Telerik,近期由于某些原因需要对数据做迁移。

由此,进入第一步:从Telerik获取数据

Telerik提供的rest api简单易用,直接通过对应的文件存储的路径,就可以获取对应的JSON数据

    class Program
    {

    /// &lt;summary&gt;
    /// // 收集下载失败的文件的信息
    /// &lt;/summary&gt;
    private static List&lt;Items&gt;
     itemsList = new List&lt;Items&gt;();

    private static string PATH = "";
    /// &lt;summary&gt;
    /// 文件总数  默认 0
    /// &lt;/summary&gt;
    private static int count = 0;
    /// &lt;summary&gt;
    /// 已下载文件数
    /// &lt;/summary&gt;
    private static int downed = 0;
    /// &lt;summary&gt;
    /// 正在下载的文件的序号
    /// &lt;/summary&gt;
    private static int downing = 0;
    /// &lt;summary&gt;
    /// 下载成功文件数
    /// &lt;/summary&gt;
    private static int succ = 0;
    /// &lt;summary&gt;
    /// 下载失败文件数
    /// &lt;/summary&gt;
    private static int erro = 0;
    /// &lt;summary&gt;
    /// 再次下载时,成功的次数
    /// &lt;/summary&gt;
    private static int succag = 0;
    /// &lt;summary&gt;
    /// 再次下载时,失败的次数
    /// &lt;/summary&gt;
    private static int erroag = 0;
    /// &lt;summary&gt;
    /// 此次需要下载的文件次数
    /// &lt;/summary&gt;
    private static int countag = 0;
    /// &lt;summary&gt;
    /// 下载失败的log路径
    /// &lt;/summary&gt;
    private static string path0 = "";
    /// &lt;summary&gt;
    /// 写入文件的字符
    /// &lt;/summary&gt;
    private static StringBuilder sb = new StringBuilder();
    static void Main(string[] args)
    {

         downlaodCC();
       //Task.Run(()=&gt; readUsers());
        Console.ReadKey();

    }
    /// &lt;summary&gt;
    /// 从指定路径URI再次下载文件
    /// &lt;/summary&gt;
    /// &lt;param name="uri"&gt;&lt;/param&gt;
    /// &lt;param name="path"&gt;&lt;/param&gt;
    public static void downag(string uri, string path)
    {
        //Console.WriteLine("第" + (downing + 1) + "个文件" + "开始下载");
        WebClient client = new WebClient();
        try
        {
            client.DownloadFile(uri, path);
            WriteLog("第" + (downing + 1) + "个文件" + uri + "下载成功");
            Console.WriteLine("第" + (downing + 1) + "个文件" + uri + "下载成功");
            succag++;
            //downed++;
        }
        catch
        {
            WriteLog("第" + (downing + 1) + "个文件" + uri + "下载失败");
            Console.WriteLine("第" + (downing + 1) + "个文件" + uri + "下载失败");
            //downsag(uri, path, 1 , (downing + 1));
            Items items = new Items();
            items.CreatedBy = "";
            items.Filename = path;
            items.Uri = uri;
            itemsList.Add(items);
            erroag++;
            sb.AppendLine("第" + downing + "个文件" + uri + "  下载失败");
        }
        downed++;
        downing++;
        if (downed == countag)
        {
            succ += succag;
            erro = erroag;
            WriteLog("第二次下载完成,共" + countag + "个文件\n" + "成功" + succag + "个;\n 失败" + erro + "个\n" + "失败率" + Convert.ToDouble((erro * 1.00 / (succag + erro))).ToString("P"));
            Console.WriteLine("第二次下载完成,共" + countag + "个文件\n" + "成功" + succag + "个;\n 失败" + erro + "个\n" + "失败率" + Convert.ToDouble((erro * 1.00 / (succag + erro))).ToString("P"));
            sb.AppendLine("总计" + count + "个文件");
            WriteLog("总计" + count + "个文件\n" + "成功" + succ + "个;\n 失败" + erro + "个\n" + "失败率" + Convert.ToDouble((erro * 1.00 / count)).ToString("P"));
            Console.WriteLine("总计" + count + "个文件\n" + "成功" + succ + "个;\n 失败" + erro + "个\n" + "失败率" + Convert.ToDouble((erro * 1.00 / count)).ToString("P"));
            sb.AppendLine("共" + erroag + "个文件下载失败");
            if (File.Exists(path0))
            {
                File.Delete(path0);
            }
            File.WriteAllText(path0, sb.ToString());
            // 控制失败率为1% 进行循环下载
            if ((erro * 1.00 / count) &gt; 0.01)
            {
                showErro();
            }
            else
            {
                readUsers();
            }

        }
    }

    /// &lt;summary&gt;
    /// 此方法目前未用
    /// &lt;/summary&gt;
    /// &lt;param name="uri"&gt;&lt;/param&gt;
    /// &lt;param name="path"&gt;&lt;/param&gt;
    /// &lt;param name="times"&gt;&lt;/param&gt;
    /// &lt;param name="downingNum"&gt;&lt;/param&gt;
    public static void downsag(string uri, string path, int times, int downingNum)
    {
        if (times &lt; 1)
        {
            WebClient client = new WebClient();
            try
            {
                client.DownloadFile(uri, path);
                WriteLog("第" + downingNum + "个文件" + "第" + (times + 1) + "次尝试下载 " + uri + "下载成功");
                Console.WriteLine("第" + downingNum + "个文件" + "第" + (times + 1) + "次尝试下载 " + uri + "下载成功");
                succ++;
                erro--;
                downed++;
            }
            catch
            {
                WriteLog("第" + downingNum + "个文件" + "第" + (times + 1) + "次尝试下载 " + uri + "下载失败");
                Console.WriteLine("第" + downingNum + "个文件" + "第" + (times + 1) + "次尝试下载 " + uri + "下载失败");
                downsag(uri, path, (times + 1), downingNum);
            }
        }

        else
        {
            downed++;
            sb.AppendLine("第" + downingNum + "个文件" + "11次尝试下载  " + uri + "  下载失败");

        }
        if (downed == count)
        {
            WriteLog("ccccc下载完成,共" + count + "个文件\n" + "成功" + succ + "个;\n 失败" + erro + "个\n" + "失败率" + Convert.ToDouble((erro * 1.00 / count)).ToString("P"));
            Console.WriteLine("ccccc下载完成,共" + count + "个文件\n" + "成功" + succ + "个;\n 失败" + erro + "个\n" + "失败率" + Convert.ToDouble((erro * 1.00 / count)).ToString("P"));
            sb.AppendLine("共" + erro + "个文件下载失败");
            File.WriteAllText(path0, sb.ToString());
        }

    }

    public static void downlaodCC()
    {
        string appId = "";
        WriteLog("Select the app you want\n input 'b':babycare  \t input 'o':others");
        Console.WriteLine("Select the app you want\n input 'b':babycare  \t input 'o':others");
        string app = Console.ReadLine();

        if (app.Equals("b"))
        {
            appId = "kzdfjgbhdjkfghjkdfsgjksdngjklsdhgjklsdhngjfgkds";
        }
        else
        {
            WriteLog("Input your appId:");
            Console.Write("Input your appId:");
            appId = Console.ReadLine();
        }
        WriteLog("Input your File path");
        Console.WriteLine("Input your File path");
        inputPath();
        #region MyRegion
        /*string filePath = Console.ReadLine();
        PATH = filePath + @"\telerikFile";
        try
        {
            Directory.CreateDirectory(PATH);
            Console.WriteLine("创建成功");
        }
        catch
        {
            Console.WriteLine("路径错误请重新输入");
            inputPath();
        }*/
        #endregion
        //提供用于将数据发送到由 URI 标识的资源及从这样的资源接收数据的常用方法。
        WebClient wc = new WebClient();
        try
        {
            //wc.DownloadFile("http://api.everlive.com/v1/"+appId+"/Files/", @"e:\Files.txt");
            // 下载用户信息  Json
            wc.DownloadFile("http://api.everlive.com/v1/" + appId + "/Users/", PATH + @"Users.txt");
        }
        catch
        {
            WriteLog("网络不通");
            Console.WriteLine("网络不通");
        }
        //string str = System.IO.File.ReadAllText("e:\Files.txt");
        // 下载指定APP的用户文件基本信息
        string json = wc.DownloadString("http://api.everlive.com/v1/appid/Files/");
        JObject sum = (JObject)JsonConvert.DeserializeObject(json);
        // 获取文件总数
        count = int.Parse(sum["Count"].ToString());
        WriteLog("一共有" + count + "个文件");
        Console.WriteLine("一共有" + count + "个文件");
        JArray result = JArray.Parse(sum["Result"].ToString());
        for (var i = 0; i &lt; result.Count; i++)
        {
            JObject j = JObject.Parse(result[i].ToString());
            WriteLog("第" + (i + 1) + "组数据" + "开始下载");
            Console.WriteLine("第" + (i + 1) + "组数据" + "开始下载");
            string path = PATH + j["CreatedBy"].ToString() + "\\";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            WriteLog(j["CreatedBy"].ToString());
            Console.WriteLine(j["CreatedBy"].ToString());
            WriteLog(j["Uri"].ToString());
            Console.WriteLine(j["Uri"].ToString());
            WriteLog(j["Filename"].ToString());
            Console.WriteLine(j["Filename"].ToString());
            //ThreadPool.QueueUserWorkItem(new WaitCallback(dt.downs));
            Task.Run(() =&gt; downs(j["Uri"].ToString(), path + j["Filename"].ToString()));
            //路径应该设置为用户注册邮箱,设想:重新查询后,对文件夹进行重命名
            //Thread.Sleep(10);
        }

    }
    /// &lt;summary&gt;
    /// 输入保存用户文件的路径
    /// &lt;/summary&gt;
    public static void inputPath()
    {
        string ss = Console.ReadLine();
        PATH = ss + @"\telerikFile\";
        try
        {
            Directory.CreateDirectory(PATH);
            WriteLog("路径可用\n正在继续");
            Console.WriteLine("路径可用\n正在继续");
            path0 = PATH + @"\log.txt";
        }
        catch
        {
            WriteLog("路径错误请重新输入");
            Console.WriteLine("路径错误请重新输入");
            inputPath();
        }
    }

    public static void showErro()
    {
        WriteLog("再次尝试下载");
        Thread.Sleep(3000);
        Console.WriteLine("第二次尝试下载");
        int i = 1;
        sb = new StringBuilder();
        erroag = 0;
        succag = 0;
        countag = erro;
        downing = 0;
        downed = 0;

        /* string ins = Console.ReadLine();
         if (ins.Equals("y")) { */
        List&lt;Items&gt; itemsListag = itemsList;
        itemsList = new List&lt;Items&gt;();
        foreach (Items items in itemsListag)
        {
            WriteLog("第" + i + "个文件" + items.Uri + "开始下载");
            Console.WriteLine("第" + i + "个文件" + items.Uri + "开始下载");

            //Console.WriteLine("第"+i+"个失败文件"+items.CreatedBy);
            //Console.WriteLine(items.Filename);
            //Console.WriteLine(items.Uri);
            Task.Run(() =&gt; downag(items.Uri, items.Filename));
            i++;
            //   }
        }
    }
    /// &lt;summary&gt;
    /// 获取用户
    /// &lt;/summary&gt;
    public static void readUsers()
    {
        Object obj = new object();
        lock(obj)
        {
            WebClient wc = new WebClient();
            //wc.DownloadFile("http://api.everlive.com/v1/appid/Users/", PATH + @"Users.txt");
            string str = System.IO.File.ReadAllText(@"E:\telerikFile\Users.txt");
            JObject sum = (JObject)JsonConvert.DeserializeObject(str);
            count = int.Parse(sum["Count"].ToString());
            WriteLog("一共有" + count + "用户");
            Console.WriteLine("一共有" + count + "用户");
            JArray result = JArray.Parse(sum["Result"].ToString());
            for (var i = 0; i &lt; result.Count; i++)

            {
                JObject j = JObject.Parse(result[i].ToString());
                WriteLog("第" + (i + 1) + "个用户");
                WriteLog(j["Email"].ToString());
                WriteLog(j["Id"].ToString());
                WriteLog(j["Owner"].ToString());
                Console.WriteLine("第" + (i + 1) + "个用户");
                Console.WriteLine(j["Email"].ToString());
                Console.WriteLine(j["Id"].ToString());
                Console.WriteLine(j["Owner"].ToString());
                // 用id命名文件夹
                string pathO = PATH + j["Id"].ToString();
                // 用邮箱命名文件夹
                string pathN = PATH + j["Email"].ToString();
                Task.Run(() =&gt; rename(pathO, pathN));
            }
        }

    }
    /// &lt;summary&gt;
    /// 如果同一个用户存在两个文件夹,则合并为一个,没有做比较就之间删除文件   处理不恰当
    /// &lt;/summary&gt;
    /// &lt;param name="pathO"&gt;原文件夹&lt;/param&gt;
    /// &lt;param name="pathN"&gt;新文件夹&lt;/param&gt;
    public static void rename(string pathO, string pathN)
    {
        try
        {
            if (Directory.Exists(pathN))
            {
                Directory.Delete(pathN, true);
            }
            Directory.Move(pathO, pathN);
        }
        catch
        {
            //Console.WriteLine("重命名失败");
        }
    }
    /// &lt;summary&gt;
    /// 写入文件,做记录测试
    /// &lt;/summary&gt;
    /// &lt;param name="str"&gt;&lt;/param&gt;

    private static void WriteLog(string str)
    {
        Task.Run(() =&gt;
        {
            try
            {
                string filePath = PATH + "downloadLog.txt";
                StreamWriter sw = null;
                if (!File.Exists(filePath))
                {
                    sw = File.CreateText(filePath);
                }
                else
                {
                    sw = File.AppendText(filePath);
                }
                sw.Write(str + Environment.NewLine);//+ DateTime.Now.ToString()
                sw.Close();
            }
            catch (Exception ex)
            {
                ex.ToString();
            }
        });
    }
    }

 `</pre>
<div>
<pre>` class Items
    {   
        /// &lt;summary&gt;
        /// 数据的创建人
        /// &lt;/summary&gt;
        public string CreatedBy { get; set; }
        /// &lt;summary&gt;
        /// 数据的URI
        /// &lt;/summary&gt;
        public string Uri { get; set; }
        /// &lt;summary&gt;
        /// 文件名字
        /// &lt;/summary&gt;
        public string Filename { get; set; }

    }

</div>
</div>

存在的问题

  • 文件夹合并、文件的取舍,应该对比MD5值
  • 公共变量未加锁Lock

.NET 泛型入门简介

泛型可以简单的理解为:在类名后面后,添加用<>尖括号括起来的类型参数列表,用来定义一组“通用化”的类型

class Pair<T1,T2>
{
    public T1 First;
    public T2 Second;
}

以上Pair类的类型参数为T1,T2。T1,T2可以代表任何类型,因此只需要定义一次泛型就可以实现所有参数类型的调用。

以下为Pair类的应用实例

 Pair<int,stirng> p=new  Pair<int,stirng>
{
    First=1;
    Second=2;
}
......

int i=p.First;
string s=p.Second;

在定义泛型类型时可以对客户端代码在实例化类时用于类型参数的类型加以限制,
  • 使用Where关键字指定
  • 多个限制,使用应为”,”分开
  • new() 放到最后































    约束说明
    where T: struct类型参数必须是值类型
    where T: class类型参数必须是引用类型
    where T: new()类型参数必须有一个 public 且无参数的构造函数
    where T: <base classname>类型参数必须继承至指定的基类(base class)
    where T: <interface name>类型参数必须是指定的接口或实现了指定接口的类型
    where T: U为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数
 class Pair&lt;T1,T2&gt;  where T : class, new()
{
    public T1 First;
    public T2 Second;
}

以上限制,类型参数为

  • 参数必须是引用类型
  • 参数必有一个public且无参数的构造函数
  • [TestMethod] 
    public void TestMethod1() 
    { 
        List&lt;String&gt; names = ``new` `List&lt;String&gt;();`</div>
        names.Add( "Bruce" ); 
        names.Add( "Alfred" ); 
        names.Add( "Tim" ); 
        names.Add( "Richard" ); 
    
    names.ForEach(Print); 
    }
    
    private void Print( string s) 
    {
        Console.WriteLine(s);`</div>
    }
    
    常用的泛型有
  • List<T>列表

  • LinkedList<T>链表
  • Dictionary<TKey,TValue>字典
  • Queue<T>队列
  • Stack<T>堆栈
    参考:http://www.cnblogs.com/forgetu/archive/2011/05/08/csharp-generics.html

Blog.jobbole.com/87980

.Net中 数组、 ArrayList、List、IList数组的区别

在C#中数组,ArrayList,List都能够存储一组对象,之间必有区别

数组Array

  • 类似于线性表,在内存中是连续存储的,便于存储、取值,但一般不做插入、删除操作。
  • 存储相同类型元素的固定大小的顺序集合
  • 使用用一组连续存储单元存放数组的数据元素,存在次序约定问题
  • 大小固定
  • 谨防数组越界
  • 不建议使用数组
    //System.Array

    //1、数组[] 特定类型、固定长度

    string[] str1 = new string[3];

    str1[0] = “a”;

    str1[1] = “b”;

    str1[2] = “c”;

    Console.WriteLine(str1[2]);


    string[] str2 = new string[] { “a”, “b”, “c” };

    Console.WriteLine(str2[0]);


    string[] str3 = { “a”, “b”, “c” };

    Console.WriteLine(str3[0]);

    //2、二维数组

    //int[,] intArray=new int[2,3]{ {1,11,111},{2,22,222}};

    int[,] intArray = new int[2, 3];

    intArray[0, 0] = 1;

    intArray[0, 1] = 11;

    intArray[0, 2] = 111;


    intArray[1, 0] = 2;

    intArray[1, 1] = 22;

    intArray[1, 2] = 222;

    Console.WriteLine(“{0},{1},{2}”, intArray[0, 0], intArray[0, 1], intArray[0, 2]);

    Console.WriteLine(“{0},{1},{2}”, intArray[1, 0], intArray[1, 1], intArray[1, 2]);


    //3、多维数组

    int[, ,] intArray1 = new int[,,]

    {

    {{1, 1}, {11, 11}, {111, 111}},

    {{2, 2}, {22, 22}, {222, 222}},

    {{3, 3}, {33, 33}, {333, 333}}

    };

    Console.WriteLine(“{0},{1},{2},{3},{4},{5}”, intArray1[0, 0, 0], intArray1[0, 0, 1], intArray1[0, 1, 0], intArray1[0, 1, 1],

    intArray1[0, 2, 0], intArray1[0, 2, 1]);

    Console.WriteLine(“{0},{1},{2},{3},{4},{5}”, intArray1[1, 0, 0], intArray1[1, 0, 1], intArray1[1, 1, 0], intArray1[1, 1, 1],

    intArray1[1, 2, 0], intArray1[1, 2, 1]);

    Console.WriteLine(“{0},{1},{2},{3},{4},{5}”, intArray1[2, 0, 0], intArray1[2, 0, 1], intArray1[2, 1, 0], intArray1[2, 1, 1],

    intArray1[2, 2, 0], intArray1[2, 2, 1]);

Array.forEach 函数



Array 对象的每个元素调用指定函数。 此函数是静态的,可在不创建对象实例的情况下调用。 Firefox 除外)中,forEach 函数都忽略数组中值为 undefined 的元素


public static void ForEach<T>(
T[] array,
Action<T> action
)</pre> </div> </div> <div id="syntaxSection" class="section"> <div id="code-snippet-1" class="codeSnippetContainer"> <div class="codeSnippetContainerCodeContainer"> <div id="CodeSnippetContainerCode_1aea524e-38fa-4371-bc09-a48f4c48b531" class="codeSnippetContainerCode" dir="ltr"> <table style="height: 198px;" summary="table" width="364"> <tbody> <tr> <th scope="col"><span id="mt3" class="sentence" data-guid="b4dad0fe5fbef2c0e24d9db1cc69e5a2" data-source="">术语</span></th> <th scope="col"><span id="mt4" class="sentence" data-guid="30618b3b44fa316257d07e387759fae5" data-source="">定义</span></th> </tr> <tr> <td data-th="术语"><span class="parameter">array</span></td> <td data-th="定义"><span id="mt5" class="sentence" data-guid="948352e0d370dd0991aa6f3d5f88330c" data-source="">要枚举的 **Array** 对象。</span></td> </tr> <tr> <td data-th="术语"><span class="parameter">method</span></td> <td data-th="定义"><span id="mt6" class="sentence" data-guid="fba19d57213fe41e88b2d51c372d753c" data-source="">要对数组中每个元素调用的函数。</span></td> </tr> </tbody> </table> <div> <pre>public class SamplesArray
{
public static void Main()
{
// create a three element array of integers
int[] intArray = new int[] {2, 3, 4};

// set a delegate for the ShowSquares method
Action<int> action = new Action<int>(ShowSquares);

Array.ForEach(intArray, action);
}

private static void ShowSquares(int val)
{
Console.WriteLine(“{0:d} squared = {1:d}”, val, valval);
}
}

/

This code produces the following output:

2 squared = 4
3 squared = 9
4 squared = 16
*/`





## ArrayList
  • 在使用该类时必须进行引用(装箱、拆箱)
  • ArrayList对象用来克服Array的缺点
  • 大小是按照其中存储的数据来动态扩充与收缩
  • ArrayList会把所有插入其中的数据当作为object类型来处理
  • 一个对象可存储多种类型的数据
  • 可能会报类型不匹配的错误 不安全
  • 通过调用TrimToSize、设置Capacity属性减少容量
  • 可通过调用 TrimToSize 或通过显式设置 Capacity(容量) 属性减少容量。


    public static void Main() {


    ArrayList myAL = new ArrayList();

    myAL.Add(“Hello”);

    myAL.Add(“World”);

    myAL.Add(“!”);


    Console.WriteLine( “myAL” );

    Console.WriteLine( “ Count: {0}”, myAL.Count );

    Console.WriteLine( “ Capacity: {0}”, myAL.Capacity );

    Console.Write( “ Values:” );

    PrintValues( myAL );

    }


    public static void PrintValues( IEnumerable myList ) {

    foreach ( Object obj in myList )

    Console.Write( “ {0}”, obj );

    Console.WriteLine();

    Console.ReadKey();

    }

    泛型List<T>

  • 解决ArrayList的缺点

  • ArrayList 类的泛型等效类
  • 大小可按需动态增加
  • 必须为其声明List集合内数据的对象类型
  • 可用委托<>


    `(1)声明 List<T>mlist = new List<T>();
    eg: string[] Arr = {“a”,”b”,”c”};

    List&lt;string&gt; mlist = new List&lt;string&gt;(Arr);
    

    (2)添加一个元素 List.Add(T item) 
    eg: mlist.Add(“d”);
    (3)添加集合元素
    eg: string[] Arr2 ={“f”,”g”.”h”};

    mlist.AddRange(Arr2);
    

    (4)在index位置添加一个元素 Insert(int index,T item)
    eg: mlist.Insert(1,”p”);
    (5)遍历List中元素
      foreach(T element in mlist) T的类型与mlist声明时一样
         {
           Console.WriteLine(element);

          }
    eg:
    foreach(string s in mlist)
          {
             Console.WriteLine(s);
           }
    

    (6)删除元素

    List.Remove(T item) 删除一个值
    eg: mlist.Remove("a");
    List.RemoveAt(int index);删除下标为index的元素
    eg: mlist.RemoveAt(0);
    
    List.RemoveRange(int index,int count); 下标index开始,删除count个元素
    eg:mlist.RemoveRange(3,2);
    

    (7)判断某个元素是否在该List中

    List.Contains(T item) 返回true或false
    eg:
    if(mlist.Contains"("g"))
       Console.WriteLine("g存在列表中");
    else
       mlist.Add("g");
    

    (8)给List里面元素排序 List.Sort() 默认是元素每一个字母按升序
    eg: mlist.Sort();
    (9)给List里面元素顺序反转 List.Reverse() 可以与List.Sort()配合使用
    (10)List清空 List.Clear()
    eg: mlist.Clear();
    (11)获得List中元素数目 List.Count() 返回int值
    eg: mlist.count();


 

总结

http://blog.csdn.net/zhangxinbin5/article/details/8987277

blog.jobbole.com/102452

https://msdn.microsoft.com/en-us/library/zecdkyw2(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

 

|