C#应该掌握的一些东西

 

随着培训机构的增多,越来越多的人进入IT行业。那么对于我们这些自学出来,经验不够丰富的转行者来说,我们需要掌握最起码的一些东西,这对于面试很有用,而且在工作中也很常用。本人在学习了网上各位大神关于一些知识点的总结和研究后,总结了这么一套东西。祝大家都能工作顺利,找到自己满意的工作。

  1. 重载和重写

重写:当一个子类继承一父类,而子类中的方法与父类中的方法的名称,参数个数、类型都完全一致时,就称子类中的这个方法重写了父类中的方法。

重载:一个类中的方法与另一个方法同名,但是参数表不同,这种方法称之为重载方法。

  1. 面向对象

封装:那么封装是什么呢? 封装就是这个人要完成一件事情,他所需要的任何工具都带在了自己的身上,所需要的技术也都装在了自己的脑子里了。 每个对象都包含它能进行操作所需要的所有信息,因此对象不必依赖其它的对象来完成自己的操作

继承:

实例化:

(1)、先父类后子类,先静态后成员;

实例化的时候先调用父类的静态构造快,在调用父类的构造方法,然后子类的构造块,在调用子类的构造方法;

(2) 、默认调用父类空构造;

重写  override

(1)、重写与重载:

重写:继承的子类中,方法签名相同( 方法名+形参个数 类型 顺序  )

重载:同一个类 方法名相同 ,形参个数 类型 顺序 不同

(2)、重写规则:在子类中签名与父类中相同,在合理范围内提高子类可见性;

A、返回类型:基本类型和void必须相同;引用类型要<=父类的返回类

B、异常:

C、可见性:要大于或等于父类中被重写的方法

多态:

有多态之前必须要有继承,只有多个类同时继承了同一个类,才有多态这样的说法。

同样一个方法,子类都有不同的实现方式,这个就是多态了,多态有助于程序的灵活性。

子类中如果重写了父类的方法(多态),那么父类中的这个方法将不会再调用。

编译时多态:其实就是重载,是对非虚成员来说的,系统在编译时,根据不同签名来决定实现何种操作。

运行时多态:也就是重写,是通过虚成员实现的,指直到系统运行时,才根据实际情况决定实现何种操作。

第二种情况是在继承的基础上实现的,子类继承基类时,通过对虚成员的重写,然后利用基类引用子类对象,那么不同的子类对象实现相应的不同操作。

这样的好处是显而易见的,利用基类类型定义一次,然后给它传入不同的子类对象,然后实现不同的操作,提高了效率。

3.值类型和引用类型

值类型:

byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。

引用类型:

string 和 class统称为引用类型。

1.值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的

是堆中存放的地址。

2.值类型存取快,引用类型存取慢。

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

4.栈的内存是自动释放的,堆内存是.NET 中会由 GC 来自动释放。

5.值类型继承自 System.ValueType,引用类型继承自 System.Object。

4.数据结构

数组Array:连续存储  
数组是最简单的数据结构。其具有如下特点:
数组存储在连续的内存上。
数组的内容都是相同类型。
数组可以直接通过下标访问。

int size = 5;
int[] test = new int[size];

//赋值
test2[0] = "chen";

//修改
test2[0] = "chenjd";

声明一个新的数组时,必须指定其长度

ArrayList:  
为了解决数组创建时必须指定长度以及只能存放相同类型的缺点而推出的数据结构。可以存储不同类型的元素。这是由于ArrayList会把它的元素都当做Object来处理。因而,加入不同类型的元素是允许的。

ArrayList test3 = new ArrayList();

//新增数据
test3.Add("chen");
test3.Add("j");
test3.Add("d");
test3.Add("is");
test3.Add(25);
//修改数据
test3[4] = 26;
//删除数据
test3.RemoveAt(4);

所谓装箱 (boxing):就是值类型实例到对象的转换那么拆箱:就是将引用类型转换为值类型。

List<T>泛型List  
为了解决ArrayList不安全类型与装箱拆箱的缺点,所以出现了泛型的概念,作为一种新的数组类型引入。和ArrayList很相似,长度都可以灵活的改变,最大的不同在于在声明List集合时,我们同时需要为其声明List集合内数据的对象类型

List<string> test4 = new List<string>();

Dictionary<K,T> 

字典的实现方式就是哈希表的实现方式,当创建字典时,必须声明key和item的类型,我们新建了一个空的字典,那么伴随而来的是2个长度为3的数组。

  1. 接口和抽象类

C#基础之接口

接口的定义是指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口。

(1) 通过接口可以实现多重继承,C#接口的成员不能有public、protected、internal、private等修饰符。原因很简单,接口里面的方法都需要由外面接口实现去实现方法体,那么其修饰符必然是public。C#接口中的成员默认是public的,java中是可以加public的。

(2) 接口成员不能有new、static、abstract、override、virtual修饰符。有一点要注意,当一个接口实现一个接口,这2个接口中有相同的方法时,可用new关键字隐藏父接口中的方法。

(3) 接口中只包含成员的签名,接口没有构造函数,所有不能直接使用new对接口进行实例化。接口中只能包含方法、属性、事件和索引的组合。接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类。

(4) C#是单继承,接口是解决C#里面类可以同时继承多个基类的问题。

class Program

{

static void Main(string[] args)

{

IWorker james1 = new James1();

IWorker james2 = new James2();

james1.work("设计");

james2.work("编程");

//从这个例子我体会到了有接口的好处,可以想象如果又来了新的员工。

//如果不采用接口,而是每个员工都有一个单独的类,这样就会容易出错。

//如果有接口这种协议约束的话,那么只要实现了接口就肯定有接口里声明的方法,我们只需拿来调用。        }

}

public interface IWorker{ void work(string s); }

class James1 : IWorker

{

public void work(string s)

{

Console.WriteLine("我的名字是James1,我的工作是" +s);

}

}

class James2 : IWorker

{

public void work(string s)

{

Console.WriteLine("我的名字是James2,我的工作是"+s);

}

}

修饰符 interface 接口名
{
  //接口主体
}
1、一个接口就相当于一个抽象类,但是它不能半喊任何实现方法。
2、接口的每种方法都必须在派生类中实现。
3、接口有时候可以看成是类的模具,它指明一个类该提供哪些内容。
4、接口主体只限于方法、索引器、属性的声明。
5、接口中不能包含字段、构造函数和常量等。
6、接口成员是隐式公开的,如果对其显式指定访问级别,就会出现编译器错误。
7、在接口中不能实现任何方法,属性或者索引器。
8、在指定方法时,只需给出返回类型、名称和参数列表,然后以分号结束。
9、实现接口的语法与实现继承一样,都用冒号“:”
示例
interface Icustomer
{
………………
}
public class MyClass: Icustomer
{
………………
}
10、接口中的方法不能重写,只能实现。
11、
编码标准:
接口名称需始终冠以大写字母I

什么是抽象类:不能被实例化的类称为抽象类,抽象类是派生类的基类。

abstract class 类名
{
…………
}
1、一个抽象类可以同时包含抽象方法和非抽象方法。
2、抽象方法只在派生类中真正实现,这表明抽象方法只存放函数原型,不涉及主体代码,
3、派生自抽象类的类需要实现其基类的抽象方法,才能实例化对象。
4、使用override关键子可在派生类中实现抽象方法,经override声明重写的方法称为重写基类方法,其签名必须与override方法的签名相同。

示例:
using System;
namespace Example_5
{
//抽象类
abstract class ABC
{
  //抽象方法
  public abstract void Afunc();
}
//派生类
class Derv:ABC
{
  //实现抽象类中的抽象方法
  public override void Afunc()
{
   Console.WriteLine(“实现抽象方法”);
  }
}
public class Test
{
  static void Main(string[] args)
  {
   Derv obj=new Derv();
   obj.Afunc();
  }
}
}
5、基类实现抽象类,则派生类不需要重新实现该抽象类。
6、抽象类并不仅仅只是一种实现技巧,它更代表一种抽象的概念,从而为所有的派生类确立一种约定。

接口和抽象类的区别

接口用于规范,抽象类用于共性。抽象类是类,所以只能被单继承,但是接口却可以一次实现多个。
接口中只能声明方法,属性,事件,索引器。而抽象类中可以有方法的实现,也可以定义非静态的类变量。
抽象类可以提供某些方法的部分实现,接口不可以。抽象类的实例是它的子类给出的。接口的实例是实现接口的类给出的。
在抽象类中加入一个方法,那么它的子类就同时有了这个方法。而在接口中加入新的方法,那么实现它的类就要重新编写(这就是为什么说接口是一个类的规范了)。
接口成员被定义为公共的,但抽象类的成员也可以是私有的、受保护的、内部的或受保护的内部成员(其中受保护的内部成员只能在应用程序的代码或派生类中访问)。此外接口不能包含字段、构造函数、析构函数、静态成员或常量。

  1. 委托和事件

委托:可以理解为将一个方法变成了参数类型,属性。下面,我们直接上代码,在代码中理解委托的运用和定义。

namespace Delegate {

//定义委托,它定义了可以代表的方法的类型

public delegate void GreetingDelegate(string name);

class Program {

Private static void EnglishGreeting(string name) {

Console.WriteLine("Morning, " + name);

}

private static void ChineseGreeting(string name)

{

Console.WriteLine("早上好, " + name);

}

//注意此方法,它接受一个GreetingDelegate类型的方法作为参数

private static void GreetPeople(string name, GreetingDelegate MakeGreeting)

{

MakeGreeting(name); }

static void Main(string[] args)

{

GreetPeople("Jimmy Zhang", EnglishGreeting); GreetPeople("张子阳", ChineseGreeting); Console.ReadKey(); }

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

事件它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。

声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。

7.设计模式

单例模式

确保一个类只有一个实例,并提供一个全局访问点。

  1. 通过定义一个静态私有变量来记录单例类的唯一实例。
  2. 私有方法来防止外界使用new关键字来创建该类实例。
  3. 公有方法来提供该类实例的唯一全局访问点
  4. 假如实例不存在则new一个新的实例,否则返回已有实例。

对于多线程来说则定义一个线程标识保证线程同步,接着加一个线程锁,多一句判断就行了。

观察者模式

Observer设计模式中主要包括如下两类对象:

  1. Subject:监视对象,它往往包含着其他对象所感兴趣的内容。
  2. Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。

Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。

下面我们开始上代码:

8.排序算法

冒泡排序:

快速排序:

一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

插入排序:

⒈ 从第一个元素开始,该元素可以认为已经被排序

⒉ 取出下一个元素,在已经排序的元素序列中从后向前扫描

⒊ 如果该元素(已排序)大于新元素,将该元素移到下一位置

⒋ 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

⒌ 将新元素插入到下一位置中

⒍ 重复步骤2~5

选择排序:

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

9.线程

1、基本概念

进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。是应用程序的一个运行例程,是应用程序的一次动态执行过程。

线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。

创建多线程的步骤:
1、编写线程所要执行的方法
2、实例化Thread类,并传入一个指向线程所要执行方法的委托。(这时线程已经产生,但还没有运行)
3、调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定

10.协同程序

一。什么是协同程序

协同程序,即在主程序运行时同时开启另一段逻辑处理,来协同当前程序的执行。换句话说,开启协同程序就是开启一个线程。

二。协同程序的开启与终止

在Unity3D中,使用MonoBehaviour.StartCoroutine方法即可开启一个协同程序,也就是说该方法必须在MonoBehaviour或继承于MonoBehaviour的类中调用。

在Unity3D中,使用StartCoroutine(string methodName)和StartCoroutine(IEnumerator routine)都可以开启一个线程。区别在于使用字符串作为参数可以开启线程并在线程结束前终止线程,相反使用IEnumerator 作为参数只能等待线程的结束而不能随时终止(除非使用StopAllCoroutines()方法);另外使用字符串作为参数时,开启线程时最多只能传递 一个参数,并且性能消耗会更大一点,而使用IEnumerator 作为参数则没有这个限制。

在Unity3D中,使用StopCoroutine(string methodName)来终止一个协同程序,使用StopAllCoroutines()来终止所有可以终止的协同程序,但这两个方法都只能终止该 MonoBehaviour中的协同程序。

还有一种方法可以终止协同程序,即将协同程序所在gameobject的active属性设置为false,当再次设置active为ture时,协同程 序并不会再开启;如是将协同程序所在脚本的enabled设置为false则不会生效。这是因为协同程序被开启后作为一个线程在运行,而 MonoBehaviour也是一个线程,他们成为互不干扰的模块,除非代码中用调用,他们共同作用于同一个对象,只有当对象不可见才能同时终止这两个线 程。

粗浅小结:

1.Coroutines顾名思议是用来协助主要进程的,在Unity中感觉就是一个可动态添加和移除的Update()函数。它的调用在所有Update函数之后。

2.yield就像是一个红绿灯,在满足紧跟在它后面的条件之前,这个协程会挂起,把执行权交给调用它的父函数,满足条件时就可以执行yield下面的代码。

协同的用法

Yield中断:(有中断就代表程序停在该处,等待yield后的时间结束再继续执行后面的语句。)

在unity C#中yield(中断)语句必须要在IEnumerator类型里

C#方法,方法的返回类型为IEnumerator,返回值如(eg: yield return new WaitForSeconds(2); 或者yield return null;)。

yields不可以在Update或者FixedUpdate里使用。

Coroutine协同:(为什么要有Coroutine的概念? 因为有中断的存在,试想一个程序都靠Yield来暂停的话,如何做到一个事件暂停的过程中,暂停过程中继续执行后面的程序? 那么就要靠Coroutine来实现。)

  • 可以在UPdate或者FixedUpdate里使用coroutine
  • 这里有两种:StartCoroutine(协同工作) 和 yield return StartCoroutine(中断式的协同工作)
  • 有yield的代表先执行完本语句(不管多长时间),或者执行完本yield方法调用,才执行后续语句。例如StartCoroutine(WaitAndPrint(2.0F)),继续执行StartCoroutine后面的语句,
  • 没有yield的代表继续顺序执行。例如:yield return StartCoroutine(WaitAndPrint(2.0F)),代表StartCoroutine(WaitAndPrint(2.0F))函数里等待全部执行完,再执行StartCoroutine后面的语句

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {

IEnumerator Start() {

print("Starting " + Time.time);

yield return StartCoroutine(WaitAndPrint(2.0F));

print("Done " + Time.time);

}

IEnumerator WaitAndPrint(float waitTime) {

yield return new WaitForSeconds(waitTime);

print("WaitAndPrint " + Time.time);

}

}

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {

void Start() {

print("Starting " + Time.time);

StartCoroutine(WaitAndPrint(2.0F));

print("Before WaitAndPrint Finishes " + Time.time);

}

IEnumerator WaitAndPrint(float waitTime) {

yield return new WaitForSeconds(waitTime);

print("WaitAndPrint " + Time.time);

}

}

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {

IEnumerator Do() {

print("Do now");

yield return new WaitForSeconds(2);

print("Do 2 seconds later");

}

IEnumerator Awake() {

yield return StartCoroutine("Do");    //Yield StartCoroutine就代表中断式的协同工作

print("Also after 2 seconds");

print("This is after the Do coroutine has finished execution");

}

}

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {public static IEnumerator Do() {

  print("Do now");

  yield return new WaitForSeconds(2);

  print("Do 2 seconds later");

}void Awake() {

  Do();    //执行DO,但是do后的语句继续执行

  print("This is printed immediately");

}

C#应该掌握的一些东西的更多相关文章

  1. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  2. iOS有关横向TableView的东西

    之前看到Apple store里面有横向的tableview,当然也有可能是collectionview啦. 尤其是项目中只有一条那么需要横向滑动的东西,就没有必要使用庞大的collectionvie ...

  3. 使用ENode框架前您需要了解的东西(初稿)

    选择ENode意味着什么可能很多人还不太清楚.我简单整理了一下: 意味着你选择了:你需要做DDD领域建模.选择了事件驱动的架构.选择了CQRS架构.选择了最终一致性.选择了事件溯源.选择了分布式.这些 ...

  4. 如何写出高质量的技术博客 这边文章出自http://www.jianshu.com/p/ae9ab21a5730 觉得不错直接拿过来了 好东西要大家分享嘛

        如何写出高质量的技术博客?答案是:如果你想,就一定能写出高质量的技术博客.看起来很唯心,但这就是事实.有足够愿力去做一件目标明确,有良好反馈系统的事情往往很简单.就是不停地训练,慢慢地,你自己 ...

  5. Intellij IDEA的一些东西

    Intellij IDEA的一些东西 2016-03-19 15:26 Ctrl + R 在当前文件进行文本替换 (必备) Ctrl + N 根据输入的 类名 查找类文件 Ctrl + Ctrl + ...

  6. 神奇的BFC以及被忽略的东西

    BFC是CSS中一个非常重要的概念,经常用来清除浮动以及处理外边距折叠,但BFC到底是个什么东西却很难准确的表达清楚,国内的相关技术文档基本都不全面,本文的目的就是对BFC的方方面面做个整理,当然未必 ...

  7. 关于这个博客以及C++入门该懂的一些东西

    给三牧中学c++入门的同学们看的博客. 大概是入门一类的?说不定会写点自己的结题报告. 写的不好/写错了别怪我,蒟蒻瑟瑟发抖. 天哪要开始写入门了我好慌那么接下来是编译器连接. (本蒟蒻喜欢用DEV ...

  8. LabVIEW 吸星大法 - 看见的好东西都是我的(上篇)

    前言 写了多年的LabVIEW程序,你是否面临这样的问题 总是在做一些重复的工作,感觉很没有意思: 总在不停的写代码,做类似的控件,实现相同的功能,丝毫没有成就感: 总在天加班,没有时间去提高自己; ...

  9. 前端er是否忽略了某些东西?——读《ppk谈JavaScript》

    关于书 “不知道ppk的网站QuirksMode,说明你可能还没有真正成为资深的JavaScript程序员.” ——Roger Johansson,瑞典资深Web专家. ppk是世界级前端技术专家,W ...

  10. 解读SDN的东西、南北向接口

    北向接口(Northbound Interface)是为厂家或运营商进行接入和管理网络的接口,即向上提供的接口. 南向接口(Southbound Interface)是提供对其他厂家网元的管理功能,支 ...

随机推荐

  1. 紫书第三章训练1 E - DNA Consensus String

    DNA (Deoxyribonucleic Acid) is the molecule which contains the genetic instructions. It consists of ...

  2. php 审批流程管理

    1.流程管理的用法是什么样的? 2.怎么发起想要的流程? 3.审批的人要是怎么审批通过? 4.流程审核是不是要挨个走过? 一.要有数据库的内容的 肯定会有表的,首先就是用户表了,然后就是流程表,用户编 ...

  3. Nginx报 No input file specified. 的问题解决之路 转

    https://m.aliyun.com/yunqi/articles/34240 今天接手公司的一个项目,照例将项目clone下来,配置本地host,nginx,然后访问. 怎么回事?迅速在php的 ...

  4. Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)

    Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码) 来源 https://blog.csdn.net/jiangwei0910410003/article/details/51 ...

  5. FZU Problem 2200 cleaning dp

    Problem Description N个人围成一圈在讨论大扫除的事情,需要选出K个人.但是每个人与他距离为2的人存在矛盾,所以这K个人中任意两个人的距离不能为2,他们想知道共有多少种方法. Inp ...

  6. iOS-文件断点续传

    * 移动客户端在和服务器交互的时候,上传和下载使用十分广泛. * 在我们下载文件的时候,我们在点击暂停的时候可以暂停下载,点击下载的时候可以继续下载,这个功能如何实现? * 下载进度条如何显示? 先大 ...

  7. 集合工具类CollectionUtils、ListUtils、SetUtils、MapUtils探究(转)

    之前一直以为集合工具类只有CollectionUtils,主要用它的isEmpty(final Collection<?> coll)静态方法来判断一个给定的集合是否为null或者是否长度 ...

  8. Codeforces989E. A Trance of Nightfall

    $n \leq 200$个平面上的点,$q \leq 200$次询问:重复操作$m \leq 10000$次,到达点$x$的概率最大是多少.操作:一开始选点$P$,不一定要是给定点,可以是平面上任一点 ...

  9. 三读bootmem【转】

    转自:http://blog.csdn.net/lights_joy/article/details/2704788 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 11  ...

  10. hdu 1787(欧拉函数)

    GCD Again Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...