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

C#程序设计语言Version2.0简介

[翻译] lover_P 2004-01-26
--------------------------------------------------------------------------------

----------------------------------------------------------------------------
本文翻译自Microsoft官方参考材料,提供给我们的计算机科学技术网的网友相互学习,任何人不得用于商业用途,转载请注明出处。
----------------------------------------------------------------------------
C# 2.0引入了很多语言扩展,最重要的就是泛型(Generics)、匿名方法(Anonymous Methods)、迭代器(Iterators)和不完全类型(Partial Types)。
• 泛型允许类、结构、接口、委托和方法通过它们所存贮和操作的数据的类型来参数化。泛型是很有用的,因为它提供了更为强大的编译期间类型检查,需要更少的数据类型之间的显式转换,并且减少了对装箱操作的需要和运行时的类型检查。
• 匿名方法允许在需要委托值时能够以“内联(in-line)”的方式书写代码块。匿名方法与Lisp语言中的拉姆达函数(lambda functions)类似。
• 迭代器是能够增量地计算和产生一系列值得方法。迭代器使得一个类能够很容易地解释foreach语句将如何迭代他的每一个元素。
• 不完全类型允许类、结构和接口被分成多个小块儿并存贮在不同的源文件中使其容易开发和维护。另外,不完全类型可以分离机器产生的代码和用户书写的部分,这使得用工具来加强产生的代码变得容易。

这一章首先对这些新特性做一个简介。简介之后有四章,提供了这些特性的完整的技术规范。
C# 2.0中的语言扩展的设计可以保证和现有代码的高度的兼容性。例如,尽管C#2.0在特定的环境中对单词where、yield和partial赋予了特殊的意义,这些单词还是可以被用作标识符。确实,C# 2.0没有增加一个会和现有代码中的标识符冲突的关键字。

19.1 泛型
泛型允许类、结构、接口、委托和方法通过它们所存贮和操作的数据的类型来参数化。C#泛型对使用Eiffel或Ada语言泛型的用户和使用C++模板的用户来说相当亲切,尽管它们也许无法忍受后者的复杂性。

19.1.1 为什么泛型?
没有泛型,一些通用的数据结构只能使用object类型来存贮各种类型的数据。例如,下面这个简单的Stack类将它的数据存放在一个object数组中,而它的两个方法,Push和Pop,分别使用object来接受和返回数据:
public class Stack
{
object[] items;
int count;
public void Push(object item) {...}
public object Pop() {...}
}
尽管使用object类型使得Stack类非常灵活,但它也不是没有缺点。例如,可以向堆栈中压入任何类型的值,譬如一个Customer实例。然而,重新取回一个值得时候,必须将Pop方法返回的值显式地转换为合适的类型,书写这些转换变更要提防运行时类型检查错误是很乏味的:
Stack stack = new Stack();
stack.Push(new Customer());
Customer c = (Customer)stack.Pop();
如果一个值类型的值,如int,传递给了Push方法,它会自动装箱。而当待会儿取回这个int值时,必须显式的类型转换进行拆箱:
Stack stack = new Stack();
stack.Push(3);
int i = (int)stack.Pop();
这种装箱和拆箱操作增加了执行的负担,因为它带来了动态内存分配和运行时类型检查。
Stack类的另外一个问题是无法强制堆栈中的数据的种类。确实,一个Customer实例可以被压入栈中,而在取回它的时候会意外地转换成一个错误的类型:
Stack stack = new Stack();
stack.Push(new Customer());
string s = (string)stack.Pop();
尽管上面的代码是Stack类的一种不正确的用法,但这段代码从技术上来说是正确的,并且不会发生编译期间错误。为题知道这段代码运行的时候才会出现,这时会抛出一个InvalidCastException异常。
Stack类无疑会从具有限定其元素类型的能力中获益。使用泛型,这将成为可能。

19.1.2 建立和使用泛型
泛型提供了一个技巧来建立带有类型参数(type parameters)的类型。下面的例子声明了一个带有类型参数T的泛型Stack类。类型参数又类名字后面的定界符“<”和“>”指定。通过某种类型建立的Stack的实例 可以无欲转换地接受该种类型的数据,这强过于与object相互装换。类型参数T扮演一个占位符的角色,直到使用时指定了一个实际的类型。注意T相当于内部数组的数据类型、Push方法接受的参数类型和Pop方法的返回值类型:
public class Stack
{
T[] items;
int count;
public void Push(T item) {...}
public T Pop() {...}
}
使用泛型类Stack时,需要指定实际的类型来替代T。下面的例子中,指定int作为参数类型T:
Stack stack = new Stack();
stack.Push(3);
int x = stack.Pop();
Stack类型称为已构造类型(constructed type)。在Stack类型中出现的所有T被替换为类型参数int。当一个Stack的实例被创建时,items数组的本地存贮是int[]而不是object[],这提供了一个实质的存贮,效率要高过非泛型的Stack。同样,Stack中的Push和Pop方法只操作int值,如果向堆栈中压入其他类型的值将会得到编译期间的错误,而且取回一个值时不必将它显示转换为原类型。
泛型可以提供强类型,这意味着例如向一个Customer对象的堆栈上压入一个int将会产生错误。这是因为Stack只能操作int值,而Stack也只能操作Customer对象。下面例子中的最后两行会导致编译器报错:
Stack stack = new Stack();
stack.Push(new Customer());
Customer c = stack.Pop();
stack.Push(3); // 类型不匹配错误
int x = stack.Pop(); // 类型不匹配错误
泛型类型的声明允许任意数目的类型参数。上面的Stack例子只有一个类型参数,但一个泛型的Dictionary类可能有两个类型参数,一个是键的类型另一个是值的类型:
public class Dictionary
{
public void Add(K key, V value) {...}
public V this[K key] {...}
}
使用Dictionary时,需要提供两个类型参数:
Dictionary dict = new Dictionary();
dict.Add("Peter", new Customer());
Customer c = dict["Peter">;

19.1.3 泛型类型实例化
和非泛型类型类似,编译过的泛型类型也由中间语言(IL, Intermediate Language)指令和元数据表示。泛型类型的IL表示当然已由类型参数进行了编码。
当程序第一次建立一个已构造的泛型类型的实例时,如Stack,.NET公共语言运行时中的即时编译器(JIT, just-in-time)将泛型IL和元数据转换为本地代码,并在进程中用实际类型代替类型参数。后面的对这个以构造的泛型类型的引用使用相同的本地代码。从泛型类型建立一个特定的构造类型的过程称为泛型类型实例化(generic type instantiation)。
.NET公共语言运行时为每个由之类型实例化的泛型类型建立一个专门的拷贝,而所有的引用类型共享一个单独的拷贝(因为,在本地代码级别上,引用知识具有相同表现的指针)。

19.1.4 约束
通常,一个泛型类不会只是存贮基于某一类型参数的数据,他还会调用给定类型的对象的方法。例如,Dictionary中的Add方法可能需要使用CompareTo方法来比较键值:
public class Dictionary
{
public void Add(K key, V value)
{
...
if (key.CompareTo(x) < 0) {...} // 错误,没有CompareTo方法
...
}
}
由于指定的类型参数K可以是任何类型,可以假定存在的参数key具有的成员只有来自object的成员,如Equals、GetHashCode和ToString;因此上面的例子会发生编译错误。当然可以将参数key转换成为一具有CompareTo方法的类型。例如,参数key可以转换为IComparable:
public class Dictionary
{
public void Add(K key, V value)
{
...
if (((IComparable)key).CompareTo(x) < 0) {...}
...
}
}
当这种方案工作时,会在运行时引起动态类型转换,会增加开销。更要命的是,它还可能将错误报告推迟到运行时。如果一个键没有实现IComparable接口,会抛出InvalidCastException异常。
为了提供更强大的编译期间类型检查和减少类型转换,C#允许一个可选的为每个类型参数提供的约束(constraints)列表。一个类型参数的约束指定了一个类型必须遵守的要求,使得这个类型参数能够作为一个变量来使用。约束由关键字where来声明,后跟类型参数的名字,再后是一个类或接口类型的列表,或构造器约束new()。
要想使Dictionary类能保证键值始终实现了IComparable接口,类的声明中应该对类型参数K指定一个约束:
public class Dictionary where K: IComparable
{
public void Add(K key, V value)
{
...
if (key.CompareTo(x) < 0) {...}
...
}
}
通过这个声明,编译器能够保证所有提供给类型参数K的类型都实现了IComparable接口。进而,在调用CompareTo方法前不再需要将键值显式转换为一个IComparable接口;一个受约束的类型参数类型的值的所有成员都可以直接使用。
对于给定的类型参数,可以指定任意数目的接口作为约束,但只能指定一个类(作为约束)。每一个被约束的类型参数都有一个独立的where子句。在下面的例子中,类型参数K有两个接口约束,而类型参数E有一个类约束和一个构造器约束:
public class EntityTable
where K: IComparable, IPersistable
where E: Entity, new()
{
public void Add(K key, E entity)
{
...
if (key.CompareTo(x) < 0) {...}
...
}
}
上面例子中的构造器约束,new(),保证了作为的E类型变量的类型具有一个公共、无参的构造器,并允许泛型类使用new E()来建立该类型的一个实例。
类型参数约束的使用要小心。尽管它们提供了更强大的编译期间类型检查并在一些情况下改进了性能,它还是限制了泛型类型的使用。例如,一个泛型类List可能约束T实现IComparable接口以便Sort方法能够比较其中的元素。然而,这么做使List不能用于那些没有实现IComparable接口的类型,尽管在这种情况下Sort方法从来没被实际调用过。

19.1.5 泛型方法
有的时候一个类型参数并不是整个类所必需的,而只用于一个特定的方法中。通常,这种情况发生在建立一个需要一个泛型类型作为参数的方法时。例如,在使用前面描述过的Stack类时,一种公共的模式就是在一行中压入多个值,如果写一个方法通过单独调用它类完成这一工作会很方便。对于一个特定的构造过的类型,如Stack,这个方法看起来会是这样:
void PushMultiple(Stack stack, params int[] values) {
foreach (int value in values) stack.Push(value);
}
这个方法可以用于将多个int值压入一个Stack:
Stack stack = new Stack();
PushMultiple(stack, 1, 2, 3, 4);
然而,上面的方法只能工作于特定的构造过的类型Stack。要想使他工作于任何Stack,这个方法必须写成泛型方法(generic method)。一个泛型方法有一个或多个类型参数,有方法名后面的“<”和“>”限定符指定。这个类型参数可以用在参数列表、返回至和方法体中。一个泛型的PushMultiple方法看起来会是这样:
void PushMultiple(Stack stack, params T[] values) {
foreach (T value in values) stack.Push(value);
}
使用这个方法,可以将多个元素压入任何Stack中。当调用一个泛型方法时,要在函数的调用中将类型参数放入尖括号中。例如:
Stack stack = new Stack();
PushMultiple(stack, 1, 2, 3, 4);
这个泛型的PushMultiple方法比上面的版本更具可重用性,因为它能工作于任何Stack,但这看起来并不舒服,因为必须为T提供一个类型参数。然而,很多时候编译器可以通过传递给方法的其他参数来推断出正确的类型参数,这个过程称为类型推断(type inferencing)。在上面的例子中,由于第一个正式的参数的类型是Stack,并且后面的参数类型都是int,编译器可以认定类型参数一定是int。因此,在调用泛型的PushMultiple方法时可以不用提供类型参数:
Stack stack = new Stack();
PushMultiple(stack, 1, 2, 3, 4);

19.2 匿名方法
实践处理方法和其他回调方法通常需要通过专门的委托来调用,而不是直接调用。因此,迄今为止我们还只能将一个实践处理和回调的代码放在一个具体的方法中,再为其显式地建立委托。相反,匿名方法(anonymous methods)允许将与一个委托关联的代码“内联(in-line)”到使用委托的地方,我们可以很方便地将代码直接写在委托实例中。除了看起来舒服,匿名方法还共享对本地语句所包含的函数成员的访问。如果想在命名方法(区别于匿名方法)中达成这种共享,需要手动创建一个辅助类并将本地成员“提升(lifting)”到这个类的域中。
下面的例子展示了从一个包含一个列表框、一个文本框和一个按钮的窗体中获取一个简单的输入。当按钮按下时文本框中的文本会被添加到列表框中。
class InputForm: Form
{
ListBox listBox;
TextBox textBox;
Button addButton;
public MyForm() {
listBox = new ListBox(...);
textBox = new TextBox(...);
addButton = new Button(...);
addButton.Click += new EventHandler(AddClick);
}
void AddClick(object sender, EventArgs e) {
listBox.Items.Add(textBox.Text);
}
}
尽管对按钮的Click事件的响应只有一条语句,这条语句也必须放到一个独立的具有完整的参数列表的方法中,并且要手动创建引用该方法的EventHandler委托。使用匿名方法,事件处理的代码会变得更加简洁:
class InputForm: Form
{
ListBox listBox;
TextBox textBox;
Button addButton;
public MyForm() {
listBox = new ListBox(...);
textBox = new TextBox(...);
addButton = new Button(...);
addButton.Click += delegate {
listBox.Items.Add(textBox.Text);
};
}
}
一个匿名方法由关键字delegate和一个可选的参数列表组成,并将语句放入“{”和“}”限定符中。前面例子中的匿名方法没有使用提供给委托的参数,因此可以省略参数列表。要想访问参数,你名方法应该包含一个参数列表:
addButton.Click += delegate(object sender, EventArgs e) {
MessageBox.Show(((Button)sender).Text);
};
上面的例子中,在匿名方法和EventHandler委托类型(Click事件的类型)之间发生了一个隐式的转换。这个隐式的转换是可行的,因为这个委托的参数列表和返回值类型和匿名方法是兼容的。精确的兼容规则如下:
• 当下面条例中有一条为真时,则委托的参数列表和匿名方法是兼容的:
o 匿名方法没有参数列表且委托没有输出(out)参数。
o 匿名方法的参数列表在参数数目、类型和修饰符上与委托参数精确匹配。
• 当下面的条例中有一条为真时,委托的返回值与匿名方法兼容:
o 委托的返回值类型是void且匿名方法没有return语句或其return语句不带任何表达式。
o 委托的返回值类型不是void但和匿名方法的return语句关联的表达式的值可以被显式地转换为委托的返回值类型。
只有参数列表和返回值类型都兼容的时候,才会发生匿名类型向委托类型的隐式转换。
下面的例子使用了匿名方法对函数进行了“内联(in-lian)”。匿名方法被作为一个Function委托类型传递。
using System;
delegate double Function(double x);
class Test
{
static double[] Apply(double[] a, Function f) {
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
return result;
}
static double[] MultiplyAllBy(double[] a, double factor) {
return Apply(a, delegate(double x) { return x * factor; });
}
static void Main() {
double[] a = {0.0, 0.5, 1.0};
double[] squares = Apply(a, delegate(double x) { return x * x; });
double[] doubles = MultiplyAllBy(a, 2.0);
}
}
Apply方法需要一个给定的接受double[]元素并返回double[]作为结果的Function。在Main方法中,传递给Apply方法的第二个参数是一个匿名方法,它与Function委托类型是兼容的。这个匿名方法只简单地返回每个元素的平方值,因此调用Apply方法得到的double[]包含了a中每个值的平方值。
MultiplyAllBy方法通过将参数数组中的每一个值乘以一个给定的factor来建立一个double[]并返回。为了产生这个结果,MultiplyAllBy方法调用了Apply方法,向它传递了一个能够将参数x与factor相乘的匿名方法。
如果一个本地变量或参数的作用域包括了匿名方法,则该变量或参数称为匿名方法的外部变量(outer variables)。在MultiplyAllBy方法中,a和factor就是传递给Apply方法的匿名方法的外部变量。通常,一个局部变量的生存期被限制在块内或与之相关联的语句内。然而,一个被捕获的外部变量的生存期要扩展到至少对匿名方法的委托引用符合垃圾收集条件时。

19.2.1 方法组转换
像前面章节中描述过的那样,一个匿名方法可以被隐式转换为一个兼容的委托类型。C# 2.0允许对一组方法进行相同的转换,即所任何时候都可以省略一个委托的显式实例化。例如,下面的语句:
addButton.Click += new EventHandler(AddClick);
Apply(a, new Function(Math.Sin));
还可以写做:
addButton.Click += AddClick;
Apply(a, Math.Sin);
当使用短形式时,编译器可以自动地推断应该实例化哪一个委托类型,不过除此之外的效果都和长形式相同。

19.3 迭代器
C#中的foreach语句用于迭代一个可枚举(enumerable)的集合中的元素。为了实现可枚举,一个集合必须要有一个无参的、返回枚举器(enumerator)的GetEnumerator方法。通常,枚举器是很难实现的,因此简化枚举器的任务意义重大。
迭代器(iterator)是一块可以产生(yields)值的有序序列的语句块。迭代器通过出现的一个或多个yield语句来区别于一般的语句块:
• yield return语句产生本次迭代的下一个值。
• yield break语句指出本次迭代完成。
只要一个函数成员的返回值是一个枚举器接口(enumerator interfaces)或一个可枚举接口(enumerable interfaces),我们就可以使用迭代器:
• 所谓枚举器借口是指System.Collections.IEnumerator和从System.Collections.Generic.IEnumerator构造的类型。
• 所谓可枚举接口是指System.Collections.IEnumerable和从System.Collections.Generic.IEnumerable构造的类型。
理解迭代器并不是一种成员,而是实现一个功能成员是很重要的。一个通过迭代器实现的成员可以用一个或使用或不使用迭代器的成员覆盖或重写。
下面的Stack类使用迭代器实现了它的GetEnumerator方法。其中的迭代器按照从顶端到底端的顺序枚举了栈中的元素。
using System.Collections.Generic;
public class Stack: IEnumerable
{
T[] items;
int count;
public void Push(T data) {...}
public T Pop() {...}
public IEnumerator GetEnumerator() {
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
}
}
}
GetEnumerator方法的出现使得Stack成为一个可枚举类型,这允许Stack的实例使用foreach语句。下面的例子将值0至9压入一个整数堆栈,然后使用foreach循环按照从顶端到底端的顺序显示每一个值。
using System;
class Test
{
static void Main() {
Stack stack = new Stack();
for (int i = 0; i < 10; i++) stack.Push(i);
foreach (int i in stack) Console.Write("{0} ", i);
Console.WriteLine();
}
}
这个例子的输出为:
9 8 7 6 5 4 3 2 1 0
语句隐式地调用了集合的无参的GetEnumerator方法来得到一个枚举器。一个集合类中只能定义一个这样的无参的GetEnumerator方法,不过通常可以通过很多途径来实现枚举,包括使用参数来控制枚举。在这些情况下,一个集合可以使用迭代器来实现能够返回可枚举接口的属性和方法。例如,Stack可以引入两个新的属性——IEnumerable类型的TopToBottom和BottomToTop:
using System.Collections.Generic;
public class Stack: IEnumerable
{
T[] items;
int count;
public void Push(T data) {...}
public T Pop() {...}
public IEnumerator GetEnumerator() {
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
}
}
public IEnumerable TopToBottom {
get {
return this;
}
}
public IEnumerable BottomToTop {
get {
for (int i = 0; i < count; i++) {
yield return items[i];
}
}
}
}
TopToBottom属性的get访问器只返回this,因为堆栈本身就是一个可枚举类型。BottomToTop属性使用C#迭代器返回了一个可枚举接口。下面的例子显示了如何使用这两个属性来以任意顺序枚举栈中的元素:
using System;
class Test
{
static void Main() {
Stack stack = new Stack();
for (int i = 0; i < 10; i++) stack.Push(i);
foreach (int i in stack.TopToBottom) Console.Write("{0} ", i);
Console.WriteLine();
foreach (int i in stack.BottomToTop) Console.Write("{0} ", i);
Console.WriteLine();
}
}
当然,这些属性还可以用在foreach语句的外面。下面的例子将调用属性的结果传递给一个独立的Print方法。这个例子还展示了一个迭代器被用作一个带参的FromToBy方法的方法体:
using System;
using System.Collections.Generic;
class Test
{
static void Print(IEnumerable collection) {
foreach (int i in collection) Console.Write("{0} ", i);
Console.WriteLine();
}
static IEnumerable FromToBy(int from, int to, int by) {
for (int i = from; i <= to; i += by) {
yield return i;
}
}
static void Main() {
Stack stack = new Stack();
for (int i = 0; i < 10; i++) stack.Push(i);
Print(stack.TopToBottom);
Print(stack.BottomToTop);
Print(FromToBy(10, 20, 2));
}
}
这个例子的输出为:
9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9
10 12 14 16 18 20
泛型和非泛型的可枚举接口都只有一个单独的成员,一个无参的GetEnumerator方法,它返回一个枚举器接口。一个可枚举接口很像一个枚举器工厂(enumerator factory)。每当调用了一个正确地实现了可枚举接口的类的GetEnumerator方法时,都会产生一个独立的枚举器。
using System;
using System.Collections.Generic;
class Test
{
static IEnumerable FromTo(int from, int to) {
while (from <= to) yield return from++;
}
static void Main() {
IEnumerable e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}
上面的代码打印了一个从1到10的简单乘法表。注意FromTo方法只调用了一次用来产生可枚举接口e。而e.GetEnumerator()被调用了多次(通过foreach语句)来产生多个相同的枚举器。这些枚举器都封装了FromTo声明中指定的代码。注意,迭代其代码改变了from参数。不过,枚举器是独立的,因为对于from参数和to参数,每个枚举器拥有它自己的一份拷贝。在实现可枚举类和枚举器类时,枚举器之间的过渡状态(一个不稳定状态)是必须消除的众多细微瑕疵之一。C#中的迭代器的设计可以帮助消除这些问题,并且可以用一种简单的本能的方式来实现健壮的可枚举类和枚举器类。

19.4 不完全类型
尽管在一个单独的文件中维护一个类型的所有代码是一项很好的编程实践,但有些时候,当一个类变得非常大,这就成了一种不切实际的约束。而且,程序员经常使用代码生成器来生成一个应用程序的初始结构,然后修改产生的代码。不幸的是,当以后需要再次发布原代码的时候,现存的修正会被重写。
不完全类型允许类、结构和接口被分成多个小块儿并存贮在不同的源文件中使其容易开发和维护。另外,不完全类型可以分离机器产生的代码和用户书写的部分,这使得用工具来加强产生的代码变得容易。
要在多个部分中定义一个类型的时候,我们使用一个新的修饰符——partial。下面的例子在两个部分中实现了一个不完全类。这两个部分可能在不同的源文件中,例如第一部分可能是机器通过数据库影射工具产生的,而第二部分是手动创作的:
public partial class Customer
{
private int id;
private string name;
private string address;
private List orders;
public Customer() {
...
}
}
public partial class Customer
{
public void SubmitOrder(Order order) {
orders.Add(order);
}
public bool HasOutstandingOrders() {
return orders.Count > 0;
}
}
当上面的两部分编译到一起时,产生的代码就好像这个类被写在一个单元中一样:
public class Customer
{
private int id;
private string name;
private string address;
private List orders;
public Customer() {
...
}
public void SubmitOrder(Order order) {
orders.Add(order);
}
public bool HasOutstandingOrders() {
return orders.Count > 0;
}
}
不完全类型的所有部分必须放到一起编译,才能在编译期间将它们合并。需要特别注意的是,不完全类型并不允许扩展已编译的类型

相关文章:

腾讯布局移动应用商店 总下载量累计达40亿次

腾讯布局移动应用商店 总下载量累计达40亿次 腾讯应用中心产品总监祝涛29日在上海表示&#xff0c;移动互联网近年来在国内快速发展&#xff0c;腾讯应用中心作为国内最大的软件商店之一&#xff0c;截至目前总下载量已超过40亿次。 祝涛在当天由腾讯应用中心举行的一个开发者/…

iOS开源JSON解析库MJExtension

iOS中JSON与NSObject互转有两种方式&#xff1a;1、iOS自带类NSJSONSerialization 2、第三方开源库SBJSON、JSONKit、MJExtension。项目中一直用MJExtension来进行JSON与Model的互转&#xff0c;非常方便、强大&#xff0c;接下来介绍一下这个轻量、强大的开源库。 1、什么是MJ…

针对《评人工智能如何走向新阶段》一文,继续发布国内外的跟贴留言439-448条如下:

439&#xff0c;彩虹一号无人机实现人类永不落地的追求 日媒&#xff1a;中国亮出杀手锏 世界各国一直在研究提高飞机的续航能力 国内研制的彩虹一号无人机采用人工智能和其他高新技术&#xff0c;飞行高度30000米&#xff0c;并终于研制成功实现人类永不落地的追求。 440&a…

Asp.Net中查询域名的Whois信息

作者&#xff1a;活靶子 出处&#xff1a;AspxBoy.Com 演示http://www.aspxboy.com/whois.aspx拷贝代码请到这里 http://www.aspxboy.com/WhoisCode.htm<% Page Language"C#" %><% Import Namespace"System.Net.Sockets" %><% Import …

Python 安装 xlsx模块

为什么80%的码农都做不了架构师&#xff1f;>>> Python 安装 xlsx模块 很多时候自动化测试时测试用例是写在excel中的如何读取转换成字典是一个比较关键的问题&#xff0c;使用pip命令安装模块如下&#xff1a;pip install openpyxl 验证使用的python代码如下&…

利用WebClient和WebRequest类获得网页源代码C#

作者&#xff1a;不详 请速与本人联系 GetPageHtml.aspx<% Page language"c#" validateRequest "false" Codebehind"GetPageHtml.aspx.cs" AutoEventWireup"false" Inherits"eMeng.Exam.GetPageHtml" %><!DO…

针对《评人工智能如何走向新阶段》一文,继续发布国内外的跟贴留言449-456条如下:

449&#xff0c;IBM发布重磅产品&#xff1a;推出新的人工智能能力。IBM研发AI向用户提供自动化的运维&#xff08;生态&#xff09;服务。 IBM新任CEO Arvind Krishna5月6日在IBM举办的“Think digital”大会上说&#xff1a;IBM致力于帮助企业&#xff08;用户&#xff09;加…

POI如何使用已有Excel作为模板二三事

关于POIPOI是Apache的一个开源项目&#xff0c;起初的目标是允许用户使用java代码来对Excel进行操作&#xff0c;发展到今天POI本身支持的范围已经逐步扩展到对Microsoft Office主要产品&#xff0c;包括&#xff1a;Excel\Word\PPT\Visio的全面支持&#xff0c;目前稳定版本为…

Graphviz样例之无向图

Graphviz是一个开源软件&#xff0c;有源码&#xff0c;支持不同平台。linux系统下使用XDot程序显示dot格式的文件。 Graphviz显示图形时&#xff0c;可以选择不同的策略进行布局和布线显示图形。 Graphviz的官方网站&#xff08;点击进入&#xff09;&#xff1a;Graphviz | G…

使用command-privilege给H3C、华为设备的用户帐号授权

一、H3C设备的权限默认分为0-3这四种级别 数值越小&#xff0c;用户的级别越低 (1)访问权限0 级 : ping、tracert、telnet 等网络诊断小程序&#xff0c;不可以dis current (2)监控权限 1级: dis current、reset、可开debug这种高级系统诊断工具&#xff0c;不能进入system视图…

针对《评人工智能如何走向新阶段》一文,继续发布国内外的跟贴留言457-465条如下:

457&#xff0c;常识推理攻关进展 人工智能要变得像人一样聪明常识推理能力是必备的 机器缺乏常识推理&#xff0c;何时到了破局的时候&#xff1f;&#xff01;这是迄今为止一直困扰人工智能50多年的难题。 OpenAI于去年发布GPT—2&#xff0c;它是一个具有15亿参数的通用语…

使用 Global.asax 文件

作者&#xff1a;未知 请速与本人联系 Global.asax 文件 除了编写 UI 代码外&#xff0c;开发人员还可以将应用程序级别的逻辑和事件处理代码添加到他们的 Web 应用程序中。此代码不处理 UI 的生成&#xff0c;并且一般不为响应个别页请求而被调用。相反&#xff0c;它负…

安装hadoop下的sqoop1.99.3及配置问题全解决

2016年8月27日&#xff0c;解决sqoop先下载的是sqoop1.99.7&#xff0c;结果配置的时候&#xff0c;出现了没有server/conf目录&#xff0c;以及启动时出现无法配置错误./bin/sqoop.sh server startyqubuntu:/opt/sqoop-1.99.7$ ./bin/sqoop.sh server startSetting conf dir: …

让Asp.NET的DataGrid可排序、可选择、可分页

‘***************************************************************‘Author: 李洪根‘MAIL: lihonggen0gci-corp.com‘专栏&#xff1a; http://www.csdn.net/develop/author/netauthor/lihonggen0/‘如需引用&#xff0c;请指明出处&#xff01; CSDN论坛VB版欢迎您&#…

asp.net mvc 3 unobtrusive client side validation not working in IE

http://stackoverflow.com/questions/5909260/jquery-1-6-form-validate-not-working-in-ie7-ie8 http://stackoverflow.com/questions/7008970/asp-net-mvc-3-unobtrusive-client-side-validation-not-working-in-ie

针对《评人工智能如何走向新阶段》一文,继续发布国内外的跟贴留言477-486条如下:

477&#xff0c;国产自动驾驶操作系统首获国际认证。 华为自动驾驶操作系统&#xff08;含虚拟化机制&#xff09;己获Safety领域最高等级功能安全认证&#xff08;ISO26262ASILD&#xff09;。 这也是我国首个获得ASILD认证的操作系统内核。 自动驾驶领域已经不再是传统汽车…

TYVJ 矩阵取数 Label:高精度+dp

题目描述 帅帅经常跟同学玩一个矩阵取数游戏&#xff1a;对于一个给定的n*m的矩阵&#xff0c;矩阵中的每个元素aij均为非负整数。游戏规则如下&#xff1a; 1.每次取数时须从每行各取走一个元素&#xff0c;共n个。m次后取完矩阵所有元素&#xff1b; 2.每次取走的各个元素只能…

一个鼠标类( Using C# and Win32API)

作者&#xff1a;网际浪子 出处&#xff1a;网络 namespace ClassLibrary.Hardware { // 原创 Using C# and Win32API ( 最近我把所有的Win32API看了1遍 很是过瘾 ) public class Mouse { internal const byte SM_MOUSEPRESENT 19; internal const byte SM_CMOUSEBU…

用Python轻松搞定Excel中的20个常用操作

来源 |早起Python&#xff08;ID: zaoqi-python&#xff09;Excel与Python都是数据分析中常用的工具&#xff0c;本文将使用动态图(Excel)代码(Python)的方式来演示这两种工具是如何实现数据的读取、生成、计算、修改、统计、抽样、查找、可视化、存储等数据处理中的常用操作&a…

vb inet 一些方法

引用:http://apps.hi.baidu.com/share/detail/14791249 使用 Internet Transfer 控件 Internet Transfer 控件实现了两种广泛使用的 Internet 协议&#xff1a; 超文本传送协议&#xff08;HyperText Transfer Protocol&#xff09;(HTTP) 和文件传送协议&#xff08;File Tran…

blogs第一天

纪念一下转载于:https://www.cnblogs.com/wandezhi/p/5819780.html

技术直播:1小时突击Java工程师面试核心(限免报名)

后疫情时代&#xff0c;连程序员这个多金的职业也遭受到了一定程度的打击。从各大招聘网站和多次面试经历中&#xff0c;相信大家已经意识到&#xff0c;面试官对程序员技能体系和项目经验考核似乎更严苛了。你在面试中常常为什么苦恼呢&#xff1f;简历撰写&#xff1f;数据算…

随笔之如何实现一个线程池

为什么80%的码农都做不了架构师&#xff1f;>>> 一 缘由&#xff1a; 最近因工作问题&#xff0c;需要实现一个简单的线程池&#xff0c;满足一下要求&#xff0c; 可伸缩&#xff0c;即一旦发现线程不够用&#xff0c;则可以动态增加线程。&#xff08;至于缩减线…

url传递中文的解决方案

作者&#xff1a;xrascal 出处&#xff1a;aspxcn url传递中文的解决方案 1.设置web.config文件。&#xff08;我不喜欢设置成这样&#xff09; <system.web> ...... <globalization requestEncoding"gb2312" responseEncoding"gb2312" cult…

要哭了,模拟器键盘一直不显示

为什么80%的码农都做不了架构师&#xff1f;>>> 今天第一次遇见模拟器键盘不显示&#xff0c;导致应用对键盘的通知监听没任何卵用&#xff0c;搞了很久还是找不到原因&#xff0c;泪崩&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 最后检查到是…

用机器学习还原《隐秘的角落》那些被修改的台词

来源 | 东泽聊技术责编 | Carol前两天&#xff0c;作者一口气看完了电视剧《隐秘的角落》&#xff0c;剧情相当精彩。美中不足的是&#xff0c;剧组为了让片子过审&#xff0c; 导致11集&#xff0c;12集的剪辑顺序被打乱&#xff0c;甚至台词被修改&#xff0c;以至于片中有几…

在ASP.NET下实现数字和字符相混合的验证码

作者&#xff1a;kwklover&#xff08;原作&#xff09; 出处&#xff1a;CSDN 在ASP.NET下实现数字和字符相混合的验证码 经常在论坛里看到有问怎么实现验证码的帖子,其实关于验证码的文章在CSDN,DEV-CLUB等网站上都有很多,但是很多文章只讲诉了如何输出一个随机生成数字…

多节点CDN缓存加速系统wdcdn2.4版本发布(20111213)

多节点CDN缓存加速系统wdcdn2.4版本发布(20111213) Wdcdn是一套基于Linuxapachesquid架构开发的CDN缓存加速系统及管理系统 可帮助大中小站长或大中小企业低成本&#xff0c;快速简单&#xff0c;构建自己的CDN网络及服务器群,提供更好的在线服务,更快速的网络和网站,我们也致力…

Attribute在.net编程中的应用

作者&#xff1a;niwalker 出处&#xff1a;csdn SqlCommandGenerator类的设计 SqlCommandGEnerator类的设计思路就是通过反射得到方法的参数&#xff0c;使用被SqlCommandParameterAttribute标记的参数来装配一个Command实例。 引用的命名空间&#xff1a;//SqlCommandG…

RouterOS连载3:架设PPPoE服务

让ROS提供PPPoE服务&#xff0c;可对客户端提供类似ADSL的认证上网方式。具体设置步骤如下&#xff1a; 一、创建地址池 从Winbox联入ROS&#xff0c;单击“IP”&#xff0d;〉“Pool”,如下图 打开“IP Pool”对话框 单击“”按钮&#xff0c;添加地址池,Name: pppoepool1 …