当前位置: 首页 > 编程日记 > 正文

技术图文:C#语言中的泛型 I

C#语言中的泛型 I

知识结构:

知识结构


1. 泛型概述

泛型广泛应用于容器(Collections)和对容器操作的方法中。

从 .NET Framework2.0 开始,微软提供了一个新的命名空间System.Collections.Generic,其中包含了一些新的基于泛型的容器类。当然,我们也可以自己创建泛型类、泛型接口、泛型方法以及泛型委托,通过这种类型安全且高效的方式来满足不同应用场景的需求。

下面的示例代码以一个简单的泛型链表类作为示范。(多数情况下,推荐使用 .NET Framework 类库提供的LinkedList<T>类,而不是创建自己的链表。)

单链表结构代码如下所示:

public class SNode<T> where T : IComparable<T>
{public T Data { get; set; }public SNode<T> Next { get; set; }public SNode(T data, SNode<T> next = null){Data = data;Next = next;}
}public class SLinkList<T> : IEnumerable<T>  where T : IComparable<T>
{public SNode<T> PHead { get; protected set; }public int Length { get; private set; }public SLinkList(){Length = 0;PHead = null;}private SNode<T> Locate(int index){if (index < 0 || index > Length - 1)throw new IndexOutOfRangeException();SNode<T> temp = PHead;for (int i = 0; i < index; i++){temp = temp.Next;}return temp;}public void InsertAtFirst(T data){PHead = new SNode<T>(data, PHead);Length++;}public void InsertAtRear(T data){if (PHead == null){PHead = new SNode<T>(data);}else{Locate(Length - 1).Next = new SNode<T>(data);}Length++;}public void Insert(int index, T data){if (index < 0 || index > Length)throw new IndexOutOfRangeException();if (index == 0){InsertAtFirst(data);}else if (index == Length){InsertAtRear(data);}else{Locate(index - 1).Next = new SNode<T>(data, Locate(index));Length++;}}public void Remove(int index){if (index < 0 || index > Length - 1)throw new IndexOutOfRangeException();if (index == 0){PHead = PHead.Next;}else{Locate(index - 1).Next = Locate(index).Next;}Length--;}public T this[int index]{get{if (index < 0 || index > Length - 1)throw new IndexOutOfRangeException();return Locate(index).Data;}set{if (index < 0 || index > Length - 1)throw new IndexOutOfRangeException();Locate(index).Data = value;}}public bool IsEmpty(){return Length == 0;}public void Clear(){PHead = null;Length = 0;}public IEnumerator<T> GetEnumerator(){SNode<T> current = PHead;while (current != null){yield return current.Data;current = current.Next;}}IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();}
}

客户端代码如下所示:

class Program
{static void Main(string[] args){SLinkList<int> lst = new SLinkList<int>();for (int i = 0; i < 3; i++){lst.InsertAtFirst(i);}foreach (int i in lst){Console.WriteLine(i);}// 2// 1// 0}
}

上面实例代码演示了客户端代码如何使用泛型类SLinkList<T>,来创建一个整数链表。通过简单地改变参数的类型,很容易改写上面的代码,以创建字符串或其它自定义类型的链表。

客户端代码如下所示:

class Program
{static void Main(string[] args){SLinkList<string> lst = new SLinkList<string>();for (int i = 0; i < 3; i++){lst.InsertAtFirst("第" + i + "个,进入链表.");}foreach (string s in lst){Console.WriteLine(s);}// 第2个,进入链表.// 第1个,进入链表.// 第0个,进入链表.}
}

可见,泛型是由编译器提供的语法糖,在调用的时候,编译器针对客户端提供的类型参数生成不同的副本,以方便开发人员编码,不必为每种类型编写那些逻辑相同的代码。


2. C#泛型的优点

以前类型泛化(Generalization)是靠类型与全局基类System.Object的相互转换来实现的(里氏代换原则)。如 .NET Framework 基础类库中的ArrayList容器类。

ArrayList是一个很方便的容器类,使用中无需更改就可以存储任何引用类型或值类型。

ArrayList list1 = new ArrayList();
list1.Add(3);
list1.Add(105);
//...ArrayList list2 = new ArrayList();
list2.Add("Tom");
list2.Add("John");
//...

但是这种便利是有代价的,这需要把任何一个加入ArrayList的引用类型或值类型都隐式地向上转换成System.Object。如果这些元素是值类型,那么当加入到ArrayList中时,它们必须被装箱,当重新取回它们时,要拆箱。类型转换和装箱、拆箱的操作都降低了性能,在迭代(Iterator)大容器的情况下,装箱和拆箱对性能的影响可能十分显著。

另一个局限是缺乏编译时的类型检查,引起类型安全问题,当一个ArrayList把任意类型都转换为Object,就无法在编译时预防客户代码类似这样的操作:

ArrayList list = new ArrayList();
list.Add(3);
list.Add("Tom");
int t = 0;
foreach (int x in list)
{t += x;
}

虽然这样完全合法,但这个错误直到运行的时候才会被发现。ArrayList和其它相似的类真正需要的是一种途径,能让客户端代码在实例化之前指定所需的特定数据类型。这样就不需要向上类型转换为Object,而且编译器可以同时进行类型检查。换句话来说,ArrayList需要一个类型参数。这正是泛型所提供的,泛型即参数化类型,在编码时通过指定类型参数就不会存在装箱,拆箱问题,避免性能损失,且保证类型安全。

System.Collections.Generic命名空间中的泛型List<T>容器里,同样把元素加入容器的操作,类似这样:

List<int> list = new List<int>();
list.Add(3);
//list.Add("Tom"); //编译错误
//错误 CS1503  参数 1: 无法从“string”转换为“int”

ArrayList相比,在客户端代码中唯一增加的List<T>语法是声明和实例化中的类型参数。代码略微复杂,但创建的容器类不仅比ArrayList更安全,而且明显地更加快速,尤其当容器中的元素是值类型的时候。


3. C#泛型之类型参数

在泛型类、泛型接口、泛型方法或泛型委托的定义中,类型参数是一个占位符(Place Holder),通常为一个大写字母,如T。在客户端通过代码声明或实例化该类型的变量时,把T替换为客户端所指定的数据类型。

如果要使用泛型类SLinkList<T>,客户端代码必须在尖括号内指定一个类型参数。同样,也可以使用不同的类型参数来创建不同的对象,如下所示:

SLinkList<int> list1 = new SLinkList<int>();
SLinkList<double> list2 = new SLinkList<double>();
SLinkList<string> list3 = new SLinkList<string>();

对于这些SLinkList<T>的实例,泛型类中出现的每个T都将在编译时被类型参数指定的具体类型所取代。依靠这样的取代,我们仅用一份泛型类的代码,就创建了三个独立的,类型安全且高效的对象。


4. C#泛型之类型参数约束

通常情况下一个泛型类写成如下形式:

public class MyClass<T>
{//…
}

以上泛型类中的T,被称为未绑定类型参数(Unbounded type parameters)。

未绑定类型参数必须遵守以下规则:

  • 不能使用!===运算符,因为无法保证具体的类型参数是否能够支持这些运算符。
  • 它们可以与System.Object相互转换,也可显式地转换成任何类型。
  • 它们可以与null进行比较。如果一个未绑定类型参数与null比较,当此类型参数为值类型时,比较的结果总为false。

在设计泛型类或泛型方法时,如果对泛型成员执行任何赋值以外的操作或者调用System.Object中所没有的方法(编译器默认类型参数是直接继承System.Object),就需要在类型参数上使用约束。

利用where子句,可以为类型参数指定一个或多个约束,从而增加了类型参数可调用的属性和方法的数量。这些属性和方法受约束类型及其派生层次中的类型的支持。如果客户端代码尝试使用某个约束不允许的类型来进行实例化,则会产生编译时错误。下表列出了六类约束:

(1)值约束和引用约束举例

public class MyClass<T, U> where T : class where U : struct
{//对于多个类型参数,每个类型参数都使用一个where子句
}

注意:在应用where T : class约束时,避免对类型参数使用==!=运算符,因为这些运算符仅测试引用同一性而不测试值相等性。即使在用作参数的类型中重载这些运算符也是如此。下面的代码说明了这一点,即使string类重载==运算符,输出也为false

class Program
{private static void OpTest<T>(T s, T t) where T : class{Console.WriteLine(s == t);}static void Main(string[] args){string s1 = "target";StringBuilder sb = new StringBuilder("target");string s2 = sb.ToString();OpTest(s1, s2); // FalseConsole.WriteLine(s1 == s2); // True}
}

由于编译器在编译时仅知道T是引用类型,因此必须使用对所有引用类型都有效的默认运算符。如果必须测试值相等性,建议的方法是同时应用where T : IComparable<T>约束,并在将用于构造泛型类的任何类中实现该接口。

(2)接口约束举例

public class MyGenericClass<T> where T : IComparable<T>
{//...
}public interface IMyInterface
{//...
}public class Test
{public bool MyMethod<T>(T t) where T : IMyInterface{//...return false;}
}

(3)构造约束举例

public class ItemFactory<T> where T : new()
{public T CreateItem(){return new T();}
}
public delegate T MyDelegate<T>() where T : new();public class ItemFactory2<T> where T : IComparable, new()
{//当与其它约束一起使用时,new()约束必须最后指定。
}

(4)类型参数约束举例

internal class MyList<T>
{// T为未绑定类型参数private void Add<TU>(MyList<TU> items) where TU : T{//此处为类型参数约束}
}

类型参数也可以用在泛型类的定义中,但这种约束用途十分有限。只有希望强制两个类型参数具有继承关系的时候,才考虑使用。

public class MyClass<T, TU, TV> where T : TV
{// ...
}

下面提供一个完整的,有关泛型约束的例子。

Employee代码如下:

public class Employee : IComparable<Employee>
{public string Name { get; set; }public int Age { get; set; }public Employee(string name, int age){Name = name;Age = age;}public Employee(){;}public int CompareTo(Employee other){if (other == null)throw new ArgumentNullException();return Age - other.Age;}public override string ToString(){return Name + ":" + Age;}
}public class MyList<T> : SLinkList<T>where T : Employee, IComparable<T>, new()
{public T FindFirstOccurence(string str){T result = null;SNode<T> current = PHead;while (current != null){if (current.Data.Name == str){result = current.Data;break;}current = current.Next;}return result;}
}

客户端代码如下:

class Program
{static void Main(string[] args){// MyList<string> lst = new MyList<string>();// 编译错误                                          // 错误  CS0310  // “string”必须是具有公共的无参数构造函数的非抽象类型,// 才能用作泛型类型或方法“MyList < T >”中的参数“T”	MyList<Employee> list = new MyList<Employee>();string[] names = { "Tom", "John", "Patrick", "Maya", "Smith" };int[] ages = { 45, 19, 28, 23, 35 };for (int i = 0; i < names.Length; i++){list.InsertAtFirst(new Employee(names[i], ages[i]));}foreach (Employee e in list){Console.WriteLine(e.ToString());}// Smith:35// Maya:23// Patrick:28// John:19// Tom:45Console.WriteLine(list[0] == list[1] ? "True" : "False");// False}
}

5. C#中的泛型类

泛型类封装了不针对任何特定数据类型的操作。常用于容器类,如 .NET Framework 类库中的动态数组List<T>、双向链表LinkedList<T>、栈Stack<T>、队列Queue<T>等等。

  • 封闭构造类型的泛型类(closed constructed type):给类型参数指定类型后的泛型类,如List<int>
  • 开放构造类型的泛型类(open constructed type):未给类型参数指定类型的泛型类,如List<T>

在进行泛化的设计时,应该注意以下问题:

(1)泛型类可以继承自实体类、封闭构造类型的基类、开放构造类型的基类,如下所示。

public class BaseNode
{// 实体类
}public class BaseNodeGeneric<T>
{// 普通泛型类
}public class BaseNodeMultiple<T, TU>
{// 普通泛型类
}public class NodeConcrete<T> : BaseNode
{// 泛型类继承实体类
}public class NodeClosed<T> : BaseNodeGeneric<int>
{//泛型类继承封闭构造类型的基类
}public class NodeOpen<T> : BaseNodeGeneric<T>
{//泛型类继承开放构造类型的基类
}public class Node5<T, TU> : BaseNodeMultiple<T, TU>
{//泛型类继承开放构造类型的基类
}public class Node4<T> : BaseNodeMultiple<T, int>
{//除了与子类共用的类型参数外,必须为所有的其它类型参数指定类型
}//public class Node6<T> : BaseNodeMultiple<T, TU>
//{
//    //Generates an Error
//}

(2)非泛型的具体类可以继承自封闭构造类型的基类,但不能继承自开放构造类型的基类。这是因为客户代码无法提供基类所需的类型参数,如下所示。

public class BaseNodeGeneric<T>
{// 普通泛型类
}public class Node1 : BaseNodeGeneric<int>
{// No Error
}//public class Node2 : BaseNodeGeneric<T>
//{
//    // Generates an Error
//}//public class Node3 : T
//{
//    // Generates an Error
//}

(3)从开放式构造类型继承的泛型类,如果基类指定约束,那么子类必须指定约束,这些约束是基类约束的超集或等于基类型约束,如下所示。

public class NodeItem<T> where T : IComparable<T>, new()
{//...
}public class SpecialNodeItem<T> : NodeItem<T>where T : IComparable<T>, new()
{//...
}

(4)开放结构类型和封闭结构类型可以用作方法的参数,如下所示:

class Program
{private void Swap<T>(List<T> list1, List<T> list2){//...}private void Swap(List<int> list1, List<int> list2){//...}
}

我们来看一个综合的例子,构造SortList<T>结构。

public class SortList<T> : SLinkList<T> where T : IComparable<T>
{public void BubbleSort(){if (PHead == null || PHead.Next == null)return;bool swapped;do{swapped = false;SNode<T> previous = null;SNode<T> current = PHead;while (current.Next != null){if (current.Data.CompareTo(current.Next.Data) > 0){SNode<T> tmp = current.Next;current.Next = tmp.Next;tmp.Next = current;if (previous == null){PHead = tmp;}else{previous.Next = tmp;}previous = tmp;swapped = true;}else{previous = current;current = current.Next;}}} while (swapped);}
}

客户端代码如下:

class Program
{static void Main(string[] args){SortList<Employee> list = new SortList<Employee>();string[] names = { "Tom", "John", "Patrick", "Maya", "Smith" };int[] ages = { 45, 19, 28, 23, 35 };for (int i = 0; i < names.Length; i++){list.InsertAtFirst(new Employee(names[i], ages[i]));}foreach (Employee e in list){Console.WriteLine(e);}// Smith:35// Maya:23// Patrick:28// John:19// Tom:45list.BubbleSort();foreach (Employee e in list){Console.WriteLine(e);}// John:19// Maya:23// Patrick:28// Smith:35// Tom:45}
}

通常,我们设计泛型类是从一个已有的具体类开始的,每次把一个类型改为类型参数,直至达到一般性和可用性的最佳平衡。在设计泛型类时,需要重点考虑的事项有:

(1)考虑要把哪些类型泛化为类型参数。一般的规律是,用参数表示的类型越多,代码的灵活性和复用性也越大。但过多的泛化会导致代码难以被其它开发人员理解,需要权衡。

(2)考虑这些类型参数需要什么样的约束。一个良好的习惯是,尽可能使用最大的约束,同时保证可以处理所有需要处理的类型。例如,如果你知道你的泛型类只打算使用引用类型,那么就应用使用引用约束。这样可以防止无意中使用值类型,同时可以对T使用as运算符,并且检查空引用。

(3)考虑泛型类的继承关系,以及泛型行为应该放在基类中还是子类中。

(4)考虑泛型类是否要实现一个或多个泛型接口。例如,要设计一个泛型容器,就要考虑是否需要实现类似IEnumerable<T>IComparable<T>这样的接口,以方便容器元素的遍历和查找。


后台回复「搜搜搜」,随机获取电子资源!
欢迎关注,请扫描二维码:



相关文章:

ubuntu搭建svn、git遇到的问题及解决办法

不错的git笔记博客&#xff1a; http://www.cnblogs.com/wanqieddy/category/406859.html http://blog.csdn.net/zxncvb/article/details/22153019 Git学习教程&#xff08;六&#xff09;Git日志 http://fsjoy.blog.51cto.com/318484/245261/ 图解git http://my.oschina.net/x…

webstorm同时打开多个project方法

曾经多次碰到过想要打开多个project的时候&#xff0c;可每次打开其他项目时&#xff0c;必须选择新窗口还是替换次窗口&#xff0c;如果新窗口的话就无法跟现在的项目在同一个webstorm中同时进行编辑&#xff0c;需要来回切换窗口&#xff0c;很是不方便&#xff0c;今天无意中…

什么业务场景适合使用Redis?

Redis(Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。从2010年3月15日起&#xff0c;Redis的开发工作由VMware主持。从2013年…

Linux基础知识汇总(2)...持续更新中

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566软件安装: {软件安装的几种形式 rpm 由厂商提供二进制包 yum rpm源的前端管理器 src 源码包configure安装 bin 包含rpm和shell将安装一步执…

技术图文:C#语言中的泛型 II

C#语言中的泛型 II 知识结构&#xff1a; 6. 泛型接口 泛型类与泛型接口结合使用是很好的编程习惯&#xff0c;比如用IComparable<T>而非IComparable&#xff0c;以避免值类型上的装箱和拆箱操作。若将接口指定为类型参数的约束&#xff08;接口约束&#xff09;&#…

linux档案权限

Linux 下的档案当你对一个档案具有w权限时&#xff0c;你可以具有写入/编辑/新增/修改档案的内容的权限&#xff0c; 但并丌具备有删除该档案本身的权限&#xff01;对二档案的rwx来说&#xff0c; 主要都是针对『档案的内容』而觊&#xff0c;不档案档名的存在不否没有关系喔&…

新手UI设计师需要掌握的知识和技能

UI设计岗位在近几年的需求是越来越高的&#xff0c;很多零基础学员都开始学习UI设计技术&#xff0c;那么想要成为一名合格的UI设计师&#xff0c;新手UI设计师需要掌握的知识和技能是比较要会的&#xff0c;来看看下面的详细介绍。 新手UI设计师需要掌握的知识和技能&#xff…

数据结构与算法:04 C#面向对象设计 II

04 C#面向对象设计 II 知识结构&#xff1a; 5、属性 例1&#xff1a;属性概念的引入&#xff08;问题&#xff09; public class Animal {public int Age;public double Weight;public bool Sex;public Animal(int age, double weight, bool sex){Age age;Weight weight;S…

SharePoint迁移和升级方案

这是之前针对SharePoint迁移和升级写的方案&#xff0c;去掉了敏感的部分&#xff0c;共大家交流吧。SharePointMigrationSolution转载于:https://www.cnblogs.com/zhaojunqi/archive/2012/04/12/2444803.html

零基础如何掌握web前端开发技能

很多零基础学员想要进入到互联网行业都会选择web前端做首选技术语言来学习&#xff0c;但是学习web前端不是那么容易的&#xff0c;想要成为一名合格的web前端工程师&#xff0c;所要掌握的技能一定要会&#xff0c;下面小编就为大家详细的介绍一下零基础如何掌握web前端开发技…

数据结构与算法:05 Leetcode同步练习(一)

Leetcode同步练习&#xff08;一&#xff09; 题目01&#xff1a;两数之和 题号&#xff1a;1难度&#xff1a;简单https://leetcode-cn.com/problems/two-sum/ 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个整数&#xff0c;…

用Asp.net实现简单的文字水印

经常看见MOP上有人贴那种动态的图片&#xff0c;就是把一个字符串作为参数传给一个动态网页&#xff0c;就会生成一个带有这个字符串的图片&#xff0c;这个叫做文字水印。像什么原来的熊猫系列&#xff0c;还有后来的大树和金条&#xff0c;都挺有意思。这东西看着挺好玩的&am…

yum国内镜像

Centos-7修改yum源为国内的yum源 国外地址yum源下载慢,下到一半就断了,就这个原因就修改它为国内yum源地址 国内也就是ali 与 网易 以centos7为例 ,以 修改为阿里的yum源 先确定有wget 备份本地yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo_…

HTML的标签分为哪几类?各标签语法格式是怎样的?

HTML的标签分为哪几类?各标签语法格式是怎样的?相信大家在学习HTML课程的时候&#xff0c;有讲到这方面的知识&#xff0c;根据标签的组成特点&#xff0c;通常将HTML标签分为两大类&#xff0c;分别是“双标签”、“单标签”&#xff0c;对它们的具体介绍如下。 1.双标签 双…

数据结构与算法:06 线性表

06 线性表 知识结构&#xff1a; 1. 线性表的定义与操作 1.1 线性表的定义 线性表&#xff08;Linear List&#xff09;是由n(n≥0)n (n≥0)n(n≥0)个相同类型的数据元素a0,a1,⋯,an−1a_0,a_1,⋯,a_{n-1}a0​,a1​,⋯,an−1​组成的序列。即表中除首尾元素外&#xff0c;其…

MySQL提权简单方法

前不久网上公开了一个MySQL Func的漏洞,讲的是使用MySQL创建一个自定义的函数,然后通过这个函数来攻击服务器。最早看到相关的报道是在o-otik上,但是公布的是针对 Unix系统的Exploit,并且成功率也不是很高.而近期,国内有高手放出针对Win系统的相关文章,于是我马上找来与朋友一同…

转载LINQ优点 自己学习用的

这几天在读一本LINQ方面的书《Essential LINQ》,在这里和大家分享下。 由于对LINQ的深入总结需要大量的篇幅&#xff0c;因此在这里分成几个部分来讲。 &#xff08;*我看《Essential LINQ》是英文版的&#xff0c;有些名词不能翻译成正统的中文解释请给予谅解&#xff09; LIN…

什么是Python?好学吗?

互联网IT行业是很多人都比较关注的行业&#xff0c;大部分都想学习IT技术进入到这个行业&#xff0c;Python编程语言在近几年是多数人的选择&#xff0c;那么什么是Python?好学吗?具体来看看下面的详细介绍吧。 一、什么是python 网络上对python的解释是一门解释型、面向对象…

数据结构与算法:07 Leetcode同步练习(二)

目录 题目01&#xff1a;回文数题目02&#xff1a;x 的平方根题目03&#xff1a;爬楼梯题目04&#xff1a;买卖股票的最佳时机题目05&#xff1a;买卖股票的最佳时机 II题目06&#xff1a;跳跃游戏题目07&#xff1a;三数之和题目08&#xff1a;最接近的三数之和题目09&#x…

(五)Docker镜像和容器

之所以在之前没有讲什么是镜像和容器是因为如果你没有一个最初的认识&#xff0c;那么你就很难理解镜像和容器以及它们的区别。我相信在前面一章中的讲述中&#xff0c;你应该稍有体会容器是基于镜像构建的&#xff0c;同时构建了容器之后如果不删除就会一直存在&#xff0c;而…

翻译-高质量JavaScript代码书写基本要点(转载)

by zhangxinxu from http://www.zhangxinxu.com本文地址&#xff1a;http://www.zhangxinxu.com/wordpress/?p1173 原文作者&#xff1a;Stoyan Stefanov原文链接&#xff1a;The Essentials of Writing High Quality JavaScript 翻译编辑&#xff1a;张鑫旭//zxx: 俗不可耐的…

学习ui设计的流程是什么

UI设计在如今的市场行情中是很多企业都必不可少的一个技术岗位&#xff0c;UI设计是比较注重自身产品的用户体验的&#xff0c;想要学习UI设计&#xff0c;一定要做足功课&#xff0c;下面小编就为大家详细的介绍一下学习ui设计的流程是什么? 学习ui设计的流程是什么?总体进程…

数据结构与算法:08 Leetcode同步练习(三)

目录 题目01&#xff1a;合并两个有序链表题目02&#xff1a;删除排序链表中的重复元素题目03&#xff1a;环形链表题目04&#xff1a;反转链表题目05&#xff1a;删除链表中的节点题目06&#xff1a;两数相加题目07&#xff1a;删除链表的倒数第N个节点题目08&#xff1a;两两…

Linux之进程管理

程序是保存在外部存储设备&#xff08;如硬盘&#xff09;中的可执行机器代码和数据的集合。而进程是在CPU及内存中处于动态执行状态的计算机程序。每个程序启动后会产生一个或多个进程&#xff0c;如httpd程序&#xff0c;当有大量用户访问Web页面时&#xff0c;httpd程序会产…

win8 metro 拖拽重排grid

0.1 http://1.metrowin8.sinaapp.com/Code/index.html 拖拽重排实现思路 &#xff1a; 1.初始化拖拽对象时&#xff0c;上传拖拽对象中心点信息(包括id,className) 2.鼠标按下时&#xff0c;制造一个假的拖拽对象 3.鼠标放开时&#xff0c;计算鼠标与拖拽对象中心点的距离&…

什么是AngularJS?它有哪些特性?

AngulaJS是款非常优秀的JasSetpsn结构化框架,可以用来构建单页面应用程序&#xff0c;2009年,AngularJS由Misko Hevery等人创建&#xff0c;后来被Google收购&#xff0c;该技术已经被用于Coogle旗下的多款产品开发当中。开发人员不仅可以使用和扩展HTML语言的特性。而且可以更…

ELK安装文档及相关优化

前言&#xff1a;随着硬件成本的不断低廉&#xff0c;我们可以存储更多数据内容&#xff0c;也会对各数据加以利用&#xff0c;其中一项很重要的数据内容便是日志文件&#xff0c;无论是访问日志还是系统日志或是应用日志&#xff0c;都显得十分重要&#xff0c;而怎么加以利用…

数据结构与算法:09 栈与递归

09 栈与递归 知识结构&#xff1a; 栈是我们经常使用的一种数据结构&#xff0c;比如&#xff0c;手枪发射子弹的顺序与子弹压入弹夹的顺序是相反&#xff0c;即后压入弹夹的子弹先发射出来。又比如&#xff0c;我们使用的Word、Excel、Photoshop等软件系统中的撤销操作&#…

Javascript匿名函数

定义 匿名函数的定义非常简单&#xff1a;就是没有名字的函数。但是其用途非常的大 典型的函数定义方式 在看匿名函数之前我们先看下在Javascript中定义一个函数比较典型的几种方式 函数声明 function functionName(args) { //函数体 } 函数表达式 var functionName functi…

零基础学Java大数据难不难

java大数据如今在企业中用到的次数是非常多的&#xff0c;很多人都比较看好java技术&#xff0c;那么零基础学Java大数据难不难?想要学习java技术说难不难&#xff0c;说简单也不是很简单&#xff0c;来看看下面的详细介绍就知道了。 零基础学Java大数据难不难?因人而异&…