2020-04-08 11:50:28
摘要:1.编写第一个Go程序
首先我们编写一个Hello World程序
package main //包,表明代码所在的模块(包)
import fmt //引⼊代码依赖
//功能实现
func main() {
fmt.Println(Hello World!)
}
应用程序入口
1.必须是 main 包:package main
2.必须是 main ⽅法:func main()
3.⽂件名不⼀定是main.go
main函数的返回值和参数
与其他主要编程语⾔的差异
Go 中main函数不⽀持任何返回值
需要通过os.Exit来返回状态
同样main 函数不⽀持传⼊参数 func main(arg []string)
在程序中直接通过os.Args获取命令⾏参数
package main
import (
fmt
os
)
func main() {
if len(os.Args) 1 {
fmt.Println(Hello World, os.Args[1])
}
os.Exit(-1)
}
2. 变量与常量
在开始前先介绍一下go语言如何编写一个测试代码,方便编写测试程序。go语言只要满足下面2点规则就能运行测试
源码⽂件以_test结尾:xxx_test.go
测试⽅法名以Test开头:func TestXXX(t *testing.T) {…}
接下来我们编写一个Fibonacci 数列来介绍变量的定义
数列:1, 1, 2, 3, 5, 8, 13, ….
import (
testing
)
func TestFibList(t *testing.T) {
// 1. 变量声明一般方法
// var a int = 1
// var b int = 1
// 2. 统一一起声明,注意变量b 去掉了类型定义,系统可以根据赋值自动判断
// var (
// a int = 1
// b = 1
// )
// 3. 方法3,快捷声明与赋值(推荐)
a := 1
b := 1
t.Log(a)
for i := 0; i 5……
阅读全文
2015-05-18 22:19:39
摘要:逆变(contravariant)与协变(covariant)是C#4新增的概念,许多书籍和博客都有讲解,我觉得都没有把它们讲清楚,搞明白了它们,可以更准确地去定义泛型委托和接口,这里我尝试画图详细解析逆变与协变。
变的概念
我们都知道.Net里或者说在OO的世界里,可以安全地把子类的引用赋给父类引用,例如:
//父类 = 子类
string str = string;
object obj = str;//变了
而C#里又有泛型的概念,泛型是对类型系统的进一步抽象,比上面简单的类型高级,把上面的变化体现在泛型的参数上就是我们所说的逆变与协变的概念。通过在泛型参数上使用in或out关键字,可以得到逆变或协变的能力。下面是一些对比的例子:
协变(Foo父类 = Foo子类 ):
//泛型委托:
public delegate T MyFuncAT();//不支持逆变与协变
public delegate T MyFuncBout T();//支持协变
MyFuncAobject funcAObject = null;
MyFuncAstring funcAString = null;
MyFuncBobject funcBObject = null;
MyFuncBstring funcBString = null;
MyFuncBint funcBInt = null;
funcAObject = funcAString;//编译失败,MyFuncA不支持逆变与协变
funcBObject = funcBString;//变了,协变
funcBObject = funcBInt;//编译失败,值类型不参与协变或逆变
//泛型接口
public interface IFlyAT { }//不支持逆变与协变
public interface IFlyBout T { }//支持协变
IFlyAobject flyAObject = null;
IFlyAstring flyAString = null;
IFlyBobject flyBObject = null;
IFlyBstring flyBString = null;
IFlyBint flyBInt = null;
flyAObject = flyAString;//编译失败,IFly……
阅读全文
2015-03-06 23:15:06
摘要:什么是托管代码(managed code)
托管代码(Managed Code)就是中间语言(IL)代码,在公共语言运行库(CLR)中运行。编译器把代码编译成中间语言,当方法被调用时,CLR把具体的方法编译成适合本地计算机运行的机器码,并且将编译好的机器码缓存起来,以备下次调用使用。随着程序集的运行,CLR提供各种服务:内存管理,安全管理,线程管理,垃圾回收,类型检查等等。
托管代码是一microsoft的中间语言(IL),他主要的作用是在.NET FRAMEWORK的公共语言运行库(CLR)执行代码前去编译源代码,也就是说托管代码充当着翻译的作用,源代码在运行时分为两个阶段:
源代码编译为托管代码,(所以源代码可以有很多种,如VB,C#,J#)
托管代码编译为microsoft的平台专用语言
编译器把代码编译成中间语言(IL),而不是能直接在你的电脑上运行的机器码。中间语言被封装在一个叫程序集(assembly)的文件中,程序集中包含了描述你所创建的类,方法和属性(例如安全需求)的所有元数据。你可以拷贝这个程序集到另一台服务器上部署它。
托管代码在公共语言运行库(CLR)中运行。这个运行库给你的运行代码提供各种各样的服务,通常来说,他会加载和验证程序集,以此来保证中间语言的正确性。当某些方法被调用的时候,运行库把具体的方法编译成适合本地计算机运行的机械码,然后会把编译好的机械码缓存起来,以备下次调用。(这就是即时编译)随着程序集的运行,运行库会持续地提供各种服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。
Visual Basic .NET和C#只能产生托管代码。如果你用这类语言写程序,那么所产生的代码就是托管代码。如果你愿意,Visual C++ .NET可以生成托管代码。当你创建一个项目的时候,选择名字是以.Managed开头的项目类型。例如.Managed C++ application。
什么是非托管代码(unmanaged code)
非托管代码,直接编译成目标计算机码,在公共语言运行库环境的外部,由操作系统直接执行的代码,代码必须自己提供垃圾回收,类型检查,安全支持等服务。如需要内存管理等服务,必须显示调用操作系统的接口,通常调用Windows SDK所提供的API来实现内存管……
阅读全文
2015-02-20 15:00:38
摘要:反射是什么:
反射Reflection [rɪˈflekʃn] 提供描述程序集、模块和类型的对象(Type 类型)。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问其字段和属性。 如果代码中使用了特性,可以利用反射来访问它们。 有关更多信息,请参阅特性。
反射命名空间:System.Reflection
typeof 运算符:用于获取某个类型的 System.Type 实例
GetType():获取当前实例的 Type。
Type类常用属性
Assembly:获取在其中声明该类型的程序集。 对于泛型类型,则获取在其中定义该泛型类型的程序集。
Type类常用方法
GetMembers :获取当前 Type 的成员(包括属性、方法、字段、事件等)
GetConstructor :获取当前 Type 的特定构造函数
GetMethods:获取当前 Type 的方法
MethodInfo 类:发现方法的属性并提供对方法元数据的访问。
为什么要反射(优点):
需要访问程序元数据中的特性时。 有关详细信息,请参阅检索存储在特性中的信息。
检查和实例化程序集中的类型。
在运行时构建新类型。 使用 System.Reflection.Emit 中的类。
执行后期绑定,访问在运行时创建的类型上的方法。 请参阅主题 “动态加载和使用类型”。
反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
反射的缺点
性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用
可以通过缓存优化提高性能
使用反射会模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。 至于执行效率的话,还可以,因为它是一种强类型语言,执行效率不错。不过,建议将反射过后,保存进 cache中
反射怎么用
一般是用在反射配置以提高程序扩展、程序解耦
通过反调调用类的方法
框架中使用最多,例如MVC、WCF、WEBAPI、ORM、AOP等,这些框架就是通过反射设计,我们使用的时候可以灵活去配置文件、配置类、配置……
阅读全文
2015-02-16 11:26:53
摘要:泛型缓存字典是通过静态构造函数只被执行一次的调用机制,在首次执行时存好值,后面不再计算直接调用,以达到缓存下效果。
静态构造函数:静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。 将在创建第一个实例或引用任何静态成员之前自动调用静态构造函数。
说人话就是:只有该类第一次被调用时执行一次静态构造函数,该类后面再继续被调用时都不会再去执行静态构造函数。(跟踪调试发现确实这样,编译器能识别)
public class GenericCacheTest
{
public static void Show()
{
for (int i = 0; i 5; i++)
{
Console.WriteLine(GenericCacheint.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCachelong.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCacheDateTime.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCachestring.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCacheGenericCacheTest.GetCache());
Thread.Sleep(10);
}
}
}
/// summary
/// 字典缓存:
/// 原理:静态属性常驻内存,是key-value的hash存储,每次调用时需要去内存器查找要进行哈希运算
/// /summary
public class DictionaryCache
{
private static DictionaryType, string _TypeTimeDictionary = null;
static DictionaryCache()//静态构造函数
{
Console.WriteLine(This is DictionaryCache 静态构造函数);
_TypeTimeDictionary = new Dictio……
阅读全文
2014-11-06 21:56:17
摘要:内容导读
概述
当你声明一个变量背后发生了什么?
堆和栈
值类型和引用类型
哪些是值类型,哪些是引用类型?
装箱和拆箱
装箱和拆箱的性能问题
一、概述
本文会阐述六个重要的概念:堆、栈、值类型、引用类型、装箱和拆箱。本文首先会通过阐述当你定义一个变量之后系统内部发生的改变开始讲解,然后将关注点转移到存储双雄:堆和栈。之后,我们会探讨一下值类型和引用类型,并对有关于这两种类型的重要基础内容做一个讲解。
本文会通过一个简单的代码来展示在装箱和拆箱过程中所带来的性能上的影响,请各位仔细阅读。
二、当你声明一个变量背后发生了什么?
当你在一个.NET应用程序中定义一个变量时,在RAM中会为其分配一些内存块。这块内存有三样东西:变量的名称、变量的数据类型以及变量的值。
上面简单阐述了内存中发生的事情,但是你的变量究竟会被分配到哪种类型的内存取决于数据类型。在.NET中有两种可分配的内存:栈和堆。在接下来的几个部分中,我们会试着详细地来理解这两种类型的存储。
三、存储双雄:堆和栈
为了理解栈和堆,让我们通过以下的代码来了解背后到底发生了什么。
public void Method1()
{
// Line 1
int i=4;
// Line 2
int y=2;
//Line 3
class1 cls1 = new class1();
}
代码只有三行,现在我们可以一行一行地来了解到底内部是怎么来执行的。
**Line 1:**当这一行被执行后,编译器会在栈上分配一小块内存。栈会在负责跟踪你的应用程序中是否有运行内存需要
**Line 2:**现在将会执行第二步。正如栈的名字一样,它会将此处的一小块内存分配叠加在刚刚第一步的内存分配的顶部。你可以认为栈就是一个一个叠加起来的房间或盒子。在栈中,数据的分配和解除都会通过LIFO (Last In First Out)即先进后出的逻辑规则进行。换句话说,也就是最先进入栈中的数据项有可能最后才会出栈。
**Line 3:**在第三行中,我们创建了一个对象。当这一行被执行后,.NET会在栈中创建一个指针,而实际的对象将会存储到一个叫做“堆”的内存区域中。“堆”不会监测运行内存,它只是能够被随时访问到的一堆对象而已。不同于栈,堆用于动态内存的分配。
这里需要注意的另一个……
阅读全文
2008-08-30 14:31:19
摘要:在上一篇的示例中,我们定义了两个语法甘露,一个是Class()函数,一个是New()函数。使用Class()甘露,我们已经可以用非常优雅的格式定义一个类。例如前例中的:
var Employee = Class(Person, //派生至Person类
{
Create: function(name, age, salary)
{
Person.Create.call(this, name, age); //调用基类的构造函数
this.salary = salary;
},
ShowMeTheMoney: function()
{
alert(this.name + $ + this.salary);
}
});
这种类的写法已经和C#或Java的格式非常相似了。不过,其中调用基类的构造函数还需要用“Person.Create.call(this, name, age)”这样的方式来表达。这需要用到基类的类名,并要用call这种特殊的方式来传递this指针。这和C#的base()以及Java的super()那样的简介调用方式比起来,还需要进一步美化。
而New()函数的使用也不是很爽。前例中需要用“New(Employee, [Steve Jobs, 53, 1234])”这样的方式来创建对象,其中第一个参数是类,其他构造参数需要用数组包起来。这和JavaScript本来那种自然的“new Employee(Steve Jobs, 53, 1234)”比起来,丑陋多了。这也需要美化。
为了实现这些美化工作,我们需要回顾一下new一个对象的实质。前面我们说过:
var anObj = new aClass();
相当于先创建一个空白对象anObj,然后将其作为this指针调用aClass()函数。其实,这个过程中还有一个关键步骤就是将aClass的prototype属性,赋值给anObj内置的prototype属性。尽管我们无法访问到anObj内置的prototype属性,但它却为对象提供了可以调用的方法。
由于前例中的Class()语法甘露实际上是构造了一个原型,并将这个原型挂在了相应的原型链上。由于它返回的是一个对象而不是函数,因此由它定义出来的Per……
阅读全文
2008-08-28 11:10:18
摘要:正当我们感概万分时,天空中一道红光闪过,祥云中出现了观音菩萨。只见她手持玉净瓶,轻拂翠柳枝,洒下几滴甘露,顿时让JavaScript又添新的灵气。
观音洒下的甘露在JavaScript的世界里凝结成块,成为了一种称为“语法甘露”的东西。这种语法甘露可以让我们编写的代码看起来更象对象语言。
要想知道这“语法甘露”为何物,就请君侧耳细听。
在理解这些语法甘露之前,我们需要重新再回顾一下JavaScript构造对象的过程。
我们已经知道,用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步:第一步是建立一个新对象;第二步将该对象内置的原型对象设置为构造函数prototype引用的那个原型对象;第三步就是将该对象作为this参数调用构造函数,完成成员设置等初始化工作。对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。
那么,我们能否自己定义一个对象来当作原型,并在这个原型上描述类,然后将这个原型设置给新创建的对象,将其当作对象的类呢?我们又能否将这个原型中的一个方法当作构造函数,去初始化新建的对象呢?例如,我们定义这样一个原型对象:
var Person = //定义一个对象来作为原型类
{
Create: function(name, age) //这个当构造函数
{
this.name = name;
this.age = age;
},
SayHello: function() //定义方法
{
alert(Hello, I'm + this.name);
},
HowOld: function() //定义方法
{
alert(this.name + is + this.age + years old.);
}
};
这个JSON形式的写法多么象一个C#的类啊!既有构造函数,又有各种方法。如果可以用某种形式来创建对象,并将对象的内置的原型设置为上面这个“类”对象,不就相当于创建该类的对象了吗?
但遗憾的是,我们几乎不能访问到对象内置的原型……
阅读全文
2008-08-26 23:29:04
摘要:想必君的悟性极高,可能你会这样想:如果在JavaScript内置的那些如Object和Function等函数的prototype上添加些新的方法和属性,是不是就能扩展JavaScript的功能呢?
那么,恭喜你,你得到了!
在AJAX技术迅猛发展的今天,许多成功的AJAX项目的JavaScript运行库都大量扩展了内置函数的prototype功能。比如微软的ASP.NET AJAX,就给这些内置函数及其prototype添加了大量的新特性,从而增强了JavaScript的功能。
我们来看一段摘自MicrosoftAjax.debug.js中的代码:
String.prototype.trim = function String$trim() {
if (arguments.length !== 0) throw Error.parameterCount();
return this.replace(/^\s+|\s+$/g, '');
}
这段代码就是给内置String函数的prototype扩展了一个trim方法,于是所有的String类对象都有了trim方法了。有了这个扩展,今后要去除字符串两段的空白,就不用再分别处理了,因为任何字符串都有了这个扩展功能,只要调用即可,真的很方便。
当然,几乎很少有人去给Object的prototype添加方法,因为那会影响到所有的对象,除非在你的架构中这种方法的确是所有对象都需要的。
前两年,微软在设计AJAX类库的初期,用了一种被称为“闭包”(closure)的技术来模拟“类”。其大致模型如下:
function Person(firstName, lastName, age)
{
//私有变量:
var _firstName = firstName;
var _lastName = lastName;
//公共变量:
this.age = age;
//方法:
this.getName = function()
{
return(firstName + + lastName);
};
this.SayHello = function()
{
alert(Hello, I'm ……
阅读全文
2008-08-23 20:27:33
摘要:prototype源自法语,软件界的标准翻译为“原型”,代表事物的初始形态,也含有模型和样板的意义。
JavaScript的所有function类型的对象都有一个prototype属性。这个prototype属性本身又是一个object类型的对象,因此我们也可以给这个prototype对象添加任意的属性和方法。既然prototype是对象的“原型”,那么由该函数构造出来的对象应该都会具有这个“原型”的特性。事实上,在构造函数的prototype上定义的所有属性和方法,都是可以通过其构造的对象直接访问和调用的。也可以这么说,prototype提供了一群同类对象共享属性和方法的机制。
我们先来看看下面的代码:
function Person(name)
{
this.name = name; //设置对象属性,每个对象各自一份属性数据
};
Person.prototype.SayHello = function() //给Person函数的prototype添加SayHello方法。
{
alert(Hello, I'm + this.name);
}
var BillGates = new Person(Bill Gates); //创建BillGates对象
var SteveJobs = new Person(Steve Jobs); //创建SteveJobs对象
BillGates.SayHello(); //通过BillGates对象直接调用到SayHello方法
SteveJobs.SayHello(); //通过SteveJobs对象直接调用到SayHello方法
alert(BillGates.SayHello == SteveJobs.SayHello); //因为两个对象是共享prototype的SayHello,所以显示:true
程序运行的结果表明,构造函数的prototype上定义的方法确实可以通过对象直接调用到,而且代码是共享的。显然,把方法设置到prototype的写法显得优雅多了,尽管调用形式没有变,但逻辑上却体现了方法与类的关系,相对之前的写法,更容易理解和组织代码。
那么,对于多层次类型的构造函数情况又如何呢?
我们再来看下面的代码:
function Person(name)……
阅读全文