
暂无个人介绍
添加一个List view webpart用于显示Links列表的内容,如下: 点击Add后,如下: 使用SPD可以看到Webpart的xml definition,如下: <XmlDefinition> <View Name="{48E14159-6F55-489A-BF10-98ECF486F0A7}" MobileView="TRUE" Type="HTML" Hidden="TRUE" OrderedView="TRUE" DisplayName="" Url="/sites/team1/sub1/Part3/SitePages/testxlv.aspx" Level="1" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/images/links.png"> <Query> <OrderBy> <FieldRef Name="Order" Ascending="TRUE"/> </OrderBy> </Query> <ViewFields> <FieldRef Name="DocIcon"/> <FieldRef Name="Edit"/> <FieldRef Name="URLwMenu"/> <FieldRef Name="Comments"/> </ViewFields> <RowLimit Paged="TRUE">30</RowLimit> <Toolbar Type="Freeform"/> </View> </XmlDefinition> 选中WebPart,切换到Design的tab,然后点击Customize XSLT下拉菜单中的Customize Entire View 点击之后页面会显示出XSL的代码,可以从页面的代码行数看出,增加很多,我们就可以直接在页面上进行修改了。找到下面代码: <a onfocus="OnLink(this)" href="{$url}"> <xsl:choose> <xsl:when test="$desc=''"> <xsl:value-of disable-output-escaping="no" select="$url" /> </xsl:when> <xsl:otherwise> <xsl:value-of select="$desc" /> </xsl:otherwise> </xsl:choose> </a> 我们可以修改样式和行为,举个简单的例子,默认的link点击是在本网页打开的,我们可以将其改为在新窗口打开,我们只要将上面第一行改为如下: <a onfocus="OnLink(this)" href="javascript:void();" onclick="window.open('{$url}');"> 本文转自Justin博客园博客,原文链接:http://www.cnblogs.com/carysun/archive/2011/01/02/moss2010-cusxlv.html,如需转载请自行联系原作者
搭建一个QQ界面其实是一个很简单的实现,需要几种切换视图的控制器组合一起使用,即导航控制器、标签栏控制器、模态窗口。其中,将标签栏控制器设置为window的rootViewController,因为QQ主界面有4个控制器,分别为消息、联系人、动态、我,那么创建这4个控制器,然后再为它们分别创建一个导航控制器。此时,将之前创建的那4个控制器分别设置为对应的导航控制的rootViewcontroller。最后,将这4个导航控制器设置为标签栏控制器的子控制器即可。除此之外,我们仍然需要再创建一个登录的控制器,添加文本框输入账号和密码,如果用户输入正确,那么就以模态窗口的方式模态出主界面的窗口即可,如果输入不正确,就弹出一个提示框,给出提示信息。 具体的演示实例如下: 1.创建一个swift工程,截图为: 2.删除故事板中自带的控制器,然后创建需要的所有控制器并设置一下分组,截图为: 3.在AppDelegate.swift中设置偏好,将账号和密码归档 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { //设置偏好,将账号和密码存入沙盒 NSUserDefaults.standardUserDefaults().setValue("admin", forKey: "accountNum") NSUserDefaults.standardUserDefaults().setValue("123456", forKey: "password") //对窗体初始化 self.window = UIWindow(frame: UIScreen.mainScreen().bounds) //创建窗体的根控制器,将QQ的登录界面设置为根控制器 var rootViewController:LoginViewController? = LoginViewController() self.window!.rootViewController = rootViewController! self.window!.makeKeyAndVisible() return true } 4.在LoginViewController.swift中操作代码如下: //实现协议和声明属性 class LoginViewController: UIViewController,UIAlertViewDelegate { //声明属性(图像视图、标签、按钮、文本框) var imageView:UIImageView? var labelAccountNum:UILabel? var labelPassWord:UILabel? var loginButton:UIButton? var textFiledAccountNum:UITextField? var textFiledPassWord:UITextField? //初始化控件 override func viewDidLoad() { super.viewDidLoad() //初始化图像视图 self.imageView = UIImageView(frame: CGRectMake(38, 30, 300, 300)) self.imageView!.image = UIImage(named: "QQ.png") //初始化账号标签 self.labelAccountNum = UILabel(frame: CGRectMake(50, 400, 40, 40)) self.labelAccountNum!.text = "账号:" //初始化密码标签 self.labelPassWord = UILabel(frame: CGRectMake(50, 450, 40, 40)) self.labelPassWord!.text = "密码:" //初始化账号文本框 self.textFiledAccountNum = UITextField(frame: CGRectMake(90, 400, 200, 40)) self.textFiledAccountNum!.layer.cornerRadius = 5.0 self.textFiledAccountNum!.backgroundColor = UIColor.grayColor() self.textFiledAccountNum!.placeholder = "请输入账号" self.textFiledAccountNum!.clearsOnBeginEditing = true self.textFiledAccountNum!.becomeFirstResponder() //初始化密码文本框 self.textFiledPassWord = UITextField(frame: CGRectMake(90, 450, 200, 40)) self.textFiledPassWord!.layer.cornerRadius = 5.0 self.textFiledPassWord!.backgroundColor = UIColor.grayColor() self.textFiledPassWord!.placeholder = "请输入密码" self.textFiledPassWord!.clearsOnBeginEditing = true self.textFiledPassWord!.secureTextEntry = true //初始化登陆按钮 self.loginButton = UIButton(frame: CGRectMake(90, 500, 200, 40)) self.loginButton!.layer.cornerRadius = 8.0 self.loginButton!.setTitle("登录", forState: .Normal) self.loginButton!.backgroundColor = UIColor.purpleColor() self.loginButton!.addTarget(self, action: "LoginButtonClicked:", forControlEvents: .TouchUpInside) //设置视图颜色 self.view.backgroundColor = UIColor.whiteColor() //将控件都添加到子视图 self.view.addSubview(self.imageView!) self.view.addSubview(self.labelAccountNum!) self.view.addSubview(self.labelPassWord!) self.view.addSubview(self.textFiledAccountNum!) self.view.addSubview(self.textFiledPassWord!) self.view.addSubview(self.loginButton!) } //实现登陆事件(匹配账号和密码、实现界面的跳转) //实现登陆按钮事件 func LoginButtonClicked(sender:UIButton){ //取出偏好设置的账号和密码 let accountNum:String = NSUserDefaults.standardUserDefaults().valueForKey("accountNum")as String let passWord:String = NSUserDefaults.standardUserDefaults().valueForKey("password") as String //进行账号和密码的匹配 if self.textFiledAccountNum!.text == accountNum && self.textFiledPassWord!.text == passWord{ //创建标签栏控制器 let TabViewController:UITabBarController? = UITabBarController() TabViewController!.tabBar.backgroundColor = UIColor.darkGrayColor() //创建4个导航栏控制器 let NavgationVC1:UINavigationController? = UINavigationController() let NavgationVC2:UINavigationController? = UINavigationController() let NavgationVC3:UINavigationController? = UINavigationController() let NavgationVC4:UINavigationController? = UINavigationController() //创建4个子控制器 let newsVC:NewsViewController? = NewsViewController() let contactVC:ContactViewController? = ContactViewController() let activeVC:ActiveViewController? = ActiveViewController() let meVC:MeViewController? = MeViewController() //设置子控制器导航栏按钮标题 newsVC!.navigationItem.title = "消息" contactVC!.navigationItem.title = "联系人" activeVC!.navigationItem.title = "动态" meVC!.navigationItem.title = "自己" //设置子控制器标签栏按钮标题 newsVC!.title = "消息" contactVC!.title = "联系人" activeVC!.title = "动态" meVC!.title = "自己" //将4个子控制器分别推入对应的导航栏控制器 NavgationVC1!.pushViewController(newsVC!, animated: true) NavgationVC2!.pushViewController(contactVC!, animated: true) NavgationVC3!.pushViewController(activeVC!, animated: true) NavgationVC4!.pushViewController(meVC!, animated: true) //设置标签栏按钮字体大小、选中和未选中时的颜色、偏移位置 UITabBarItem.appearance().setTitleTextAttributes( [NSFontAttributeName:UIFont.systemFontOfSize(15),NSForegroundColorAttributeName:UIColor.purpleColor()], forState: .Normal) UITabBarItem.appearance().setTitleTextAttributes( [NSForegroundColorAttributeName:UIColor.redColor()], forState: .Selected) UITabBarItem.appearance().setTitlePositionAdjustment(UIOffsetMake(0, -10)) //往标签栏控制器添加导航栏子控制器 TabViewController!.addChildViewController(NavgationVC1!) TabViewController!.addChildViewController(NavgationVC2!) TabViewController!.addChildViewController(NavgationVC3!) TabViewController!.addChildViewController(NavgationVC4!) //模态出一个窗口 self.presentViewController(TabViewController!, animated: true, completion: nil) } //输入不正确,就弹出一个提示框 else{ var alertView:UIAlertView? = UIAlertView(title: "提示信息", message: "账号或密码输入有误", delegate: self, cancelButtonTitle: "确认") alertView!.show() } } } 5.在NewsViewcontroller.swift中创建表格,实现协议,选中单元格跳转到聊天界面 import UIKit class NewsViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { var tableView:UITableView? var arrayM:NSMutableArray? override func viewDidLoad() { super.viewDidLoad() //初始化 self.tableView = UITableView(frame: self.view.frame) self.arrayM = NSMutableArray() for i in 0..<15{ self.arrayM!.addObject("News--\(i)") } //设置数据源和代理 self.tableView!.dataSource = self self.tableView!.delegate = self //设置视图背景颜色 self.view.backgroundColor = UIColor.whiteColor() //添加到视图 self.view.addSubview(self.tableView!) } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 15 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //设置单元格重用标识符 let identifier = "Cell" //首先从队列中去取 var cell :UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(identifier) as? UITableViewCell //如果没有从队列中获取到,那么就重新创建一个 if cell == nil{ cell = UITableViewCell(style: .Subtitle, reuseIdentifier: identifier) } //设置单元格内容 cell!.textLabel!.text = String(self.arrayM!.objectAtIndex(indexPath.row) as NSString) cell!.backgroundColor = UIColor.grayColor() //返回重用单元格 return cell! } //代理协议的方法 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { //创建聊天控制器 let ChatVC:ChatViewController? = ChatViewController() //将聊天控制器压入栈中 self.navigationController!.pushViewController(ChatVC!, animated: true) //获取选中的单元 var cell:UITableViewCell? = tableView.cellForRowAtIndexPath(indexPath) cell!.selected = false } } 6.在ContactViewcontroller.swift中创建表格,实现协议,选中单元格跳转到聊天界面 import UIKit class ContactViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { var tableView:UITableView? var arrayM:NSMutableArray? override func viewDidLoad() { super.viewDidLoad() //初始化 self.tableView = UITableView(frame: self.view.frame) self.arrayM = NSMutableArray() for i in 0..<15{ self.arrayM!.addObject("Contact--\(i)") } //设置数据源和代理 self.tableView!.dataSource = self self.tableView!.delegate = self //设置视图背景颜色 self.view.backgroundColor = UIColor.whiteColor() //添加到视图 self.view.addSubview(self.tableView!) } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 15 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //设置单元格重用标识符 let identifier = "Cell" //首先从队列中去取 var cell :UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(identifier) as? UITableViewCell //如果没有从队列中获取到,那么就重新创建一个 if cell == nil{ cell = UITableViewCell(style: .Subtitle, reuseIdentifier: identifier) } //设置单元格内容 cell!.textLabel!.text = String(self.arrayM!.objectAtIndex(indexPath.row) as NSString) cell!.backgroundColor = UIColor.greenColor() //返回重用单元格 return cell! } //代理协议的方法 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { //创建聊天控制器 let ChatVC:ChatViewController? = ChatViewController() //将聊天控制器压入栈中 self.navigationController!.pushViewController(ChatVC!, animated: true) //获取选中的单元 var cell:UITableViewCell? = tableView.cellForRowAtIndexPath(indexPath) cell!.selected = false } } 7.在ActiveViewcontroller.swift中创建表格,实现协议,选中单元格跳转到聊天界面 import UIKit class ActiveViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { var tableView:UITableView? var arrayM:NSMutableArray? override func viewDidLoad() { super.viewDidLoad() //初始化 self.tableView = UITableView(frame: self.view.frame) self.arrayM = NSMutableArray() for i in 0..<15{ self.arrayM!.addObject("Active--\(i)") } //设置数据源和代理 self.tableView!.dataSource = self self.tableView!.delegate = self //设置视图背景颜色 self.view.backgroundColor = UIColor.whiteColor() //添加到视图 self.view.addSubview(self.tableView!) } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 15 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //设置单元格重用标识符 let identifier = "Cell" //首先从队列中去取 var cell :UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(identifier) as? UITableViewCell //如果没有从队列中获取到,那么就重新创建一个 if cell == nil{ cell = UITableViewCell(style: .Subtitle, reuseIdentifier: identifier) } //设置单元格内容 cell!.textLabel!.text = String(self.arrayM!.objectAtIndex(indexPath.row) as NSString) cell!.backgroundColor = UIColor.cyanColor() //返回重用单元格 return cell! } //代理协议的方法 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { //创建聊天控制器 let ChatVC:ChatViewController? = ChatViewController() //将聊天控制器压入栈中 self.navigationController!.pushViewController(ChatVC!, animated: true) //获取选中的单元 var cell:UITableViewCell? = tableView.cellForRowAtIndexPath(indexPath) cell!.selected = false } } 8.在MeViewcontroller.swift中创建表格,实现协议,选中单元格跳转到聊天界面 import UIKit class MeViewController: UIViewController,UITableViewDataSource,UITableViewDelegate { var tableView:UITableView? var arrayM:NSMutableArray? override func viewDidLoad() { super.viewDidLoad() //初始化 self.tableView = UITableView(frame: self.view.frame) self.arrayM = NSMutableArray() for i in 0..<15{ self.arrayM!.addObject("Me--\(i)") } //设置数据源和代理 self.tableView!.dataSource = self self.tableView!.delegate = self //设置视图背景颜色 self.view.backgroundColor = UIColor.whiteColor() //添加到视图 self.view.addSubview(self.tableView!) } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 15 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //设置单元格重用标识符 let identifier = "Cell" //首先从队列中去取 var cell :UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(identifier) as? UITableViewCell //如果没有从队列中获取到,那么就重新创建一个 if cell == nil{ cell = UITableViewCell(style: .Subtitle, reuseIdentifier: identifier) } //设置单元格内容 cell!.textLabel!.text = String(self.arrayM!.objectAtIndex(indexPath.row) as NSString) cell!.backgroundColor = UIColor.orangeColor() //返回重用单元格 return cell! } //代理协议的方法 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { //创建聊天控制器 let ChatVC:ChatViewController? = ChatViewController() //将聊天控制器压入栈中 self.navigationController!.pushViewController(ChatVC!, animated: true) //获取选中的单元 var cell:UITableViewCell? = tableView.cellForRowAtIndexPath(indexPath) cell!.selected = false } } 9.设置聊天界面ChatViewController.swift的背景颜色和标题 import UIKit class ChatViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.title = "聊天" self.view.backgroundColor = UIColor.purpleColor() } 演示结果截图: 开始时: 输入错误时: 输入正确后显示第一个界面: 选择第二个界面: 选择第三个界面: 选择第四个界面: 随意在一个界面,点击一个单元格,进入聊天界面: 点击自己这个按钮,返回: 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4908706.html,如需转载请自行联系原作者
200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务器超时 以下是 HTTP 状态码的完整列表。您也可以访问 HTTP 状态码上的 W3C 页以了解更多信息。 1xx 状态码 表示临时响应并需要请求者继续执行操作的状态码。 100(继续) 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 101(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。 2xx 状态码 表示成功处理了请求的状态码。 200(成功) 服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。如果针对您的 robots.txt 文件显示此状态码,则表示 Googlebot 已成功检索到该文件。 201(已创建) 请求成功并且服务器创建了新的资源。 202(已接受) 服务器已接受请求,但尚未处理。 203(非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。 204(无内容) 服务器成功处理了请求,但没有返回任何内容。 205(重置内容) 服务器成功处理了请求,但没有返回任何内容。与 204 响应不同,此响应要求请求者重置文档视图(例如,清除表单内容以输入新内容)。 206(部分内容) 服务器成功处理了部分 GET 请求。 3xx 状态码 要完成请求,需要进一步操作。通常,这些状态码用来重定向。建议您在每次请求中使用重定向不要超过 5 次。您可以使用网站管理员工具查看一下 Googlebot 在抓取重定向网页时是否遇到问题。诊断下的网络抓取页列出了由于重定向错误导致 Googlebot 无法抓取的网址。 300(多种选择) 针对请求,服务器可执行多种操作。服务器可根据请求者 (user-agent) 选择一项操作,或提供操作列表供请求者选择。 301(永久移动) 请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。您应使用此代码告诉 Googlebot 某个网页或网站已永久移动到新位置。 302(临时移动) 服务器目前从不同位置的网页响应请求,但申请人应当继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 301 代码类似,会自动将请求者转到不同的位置,但不应使用此代码来告诉 Googlebot 页面或网站已经移动,因为 Googlebot 要继续抓取原来的位置并编制索引。 303(查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。对于除 HEAD 之外的所有请求,服务器会自动转到其他位置。 304(未修改) 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。 如果网页自请求者上次请求后再也没有更改过,您应当将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。由于服务器可以告诉 Googlebot 自从上次抓取后网页没有变更,因此可节省带宽和开销。 305(使用代理) 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应当使用代理。 307(临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应当继续使用原有位置来响应以后的请求。此代码与响应 GET 和 HEAD 请求的 <a href=answer.py?answer=>301</a> 代码类似,会自动将请求者转到不同的位置,但您不应使用此代码来告诉 Googlebot 某个网页或网站已经移动,因为 Googlebot 会继续抓取原有位置并编制索引。 4xx 状态码 这些状态码表示请求可能出错,这妨碍了服务器的处理。 400(错误请求) 服务器不理解请求的语法。 401(身份验证错误) 此页要求授权。您可能不希望将此网页纳入索引。如果您的 Sitemap 中列出该网页,您可以将其删除。但如果您将其保留在您的 Sitemap 中,我们就不会抓取或索引该网页(尽管该网页将继续保持错误状态在此处列出)。如果我们将其作为搜索抓取的一部分抓取,您可以在我们的网站管理员信息中查阅其原因。 403(禁止) 服务器拒绝请求。如果您在 Googlebot 尝试抓取您网站上的有效网页时看到此状态码(可以在 Google 网站管理员工具<strong>诊断</strong>下的<strong>网络抓取< /strong>页面上看到此信息),可能是您的服务器或主机拒绝 Googlebot 访问。 404(未找到) 服务器找不到请求的网页。例如,对于服务器上不存在的网页经常会返回此代码。 如果您的网站上没有 robots.txt 文件,而您在 Google 网站管理员工具"诊断"标签的 robots.txt 页上看到此状态码,那么这是正确的状态码。但是,如果您有 robots.txt 文件而又看到此状态码,则说明您的 robots.txt 文件可能命名错误或位于错误的位置(该文件应当位于顶级域,名为 robots.txt)。 如果对于 Googlebot 尝试抓取的网址看到此状态码(在"诊断"标签的 HTTP 错误页面上),则表示 Googlebot 追踪的可能是另一个页面的无效链接(是旧链接或输入有误的链接)。 405(方法禁用) 禁用请求中指定的方法。 406(不接受) 无法使用请求的内容特性响应请求的网页。 407(需要代理授权) 此状态码与 401 类似,但指定请求者必须授权使用代理。如果服务器返回此响应,还表示请求者应当使用代理。 408(请求超时) 服务器等候请求时发生超时。 409(冲突) 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息。服务器在响应与前一个请求相冲突的 PUT 请求时可能会返回此代码,以及两个请求的差异列表。 410(已删除) 请求的资源永久删除后,服务器返回此响应。该代码与 404(未找到)代码相似,但在资源以前存在而现在不存在的情况下,有时会用来替代 404 代码。如果资源已永久删除,您应当使用 301 指定资源的新位置。 411(需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。 412(未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。 413(请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 414(请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。 415(不支持的媒体类型) 请求的格式不受请求页面的支持。 416(请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态码。 417(未满足期望值) 服务器未满足"期望"请求标头字段的要求。 5xx 状态码 这些状态码表示服务器在处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。 500(服务器内部错误) 服务器遇到错误,无法完成请求。 501(尚未实施) 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时则会返回此代码。 502(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。 503(服务不可用) 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。 504(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 505(HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。 原文地址:http://www.cnblogs.com/stu-acer/archive/2009/02/06/1385005.html 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/11/08/1598645.html,如需转载请自行联系原作者
jQuery学习笔记:核心(jQuery Core) 学习并掌握jQuery,从熟悉jQuery核心开始。 一、核心函数 1、jQuery(expression,[context]) 这个函数接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组元素。 jQuery 的核心功能都是通过这个函数实现的。 jQuery中的一切都基于这个函数,或者说都是在以某种方式使用这个函数。这个函数最基本的用法就是向它传递一个表达式(通常由 CSS 选择器组成),然后根据这个表达式来查找所有匹配的元素。默认情况下, 如果没有指定context参数,$()将在当前的 HTML 文档中查找 DOM 元素;如果指定了context 参数,如一个DOM 元素集或 jQuery 对象,那就会在这个context 中查找。返回值 jQuery参数 expression (String) : 用来查找的字符串 context (Element, jQuery) : (可选) 作为待查找的 DOM 元素集、文档或 jQuery 对象。 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(document).ready(function() { $("div>p").css("display", "block"); //显示 $("div>p").css("color", "blue"); //颜色为绿色 alert($("div>p").html()); //找到所有div元素的子元素p,并显示p元素的innerHTML var chks = $("input:checkbox", document.forms[0]); //在文档的第一个表单中,查找所有的多选按钮(即:type为checkbox的input元素) alert(chks.length); for (var i = 0; i < chks.length; i++) { alert(chks[i].checked); } }) 文档片段: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> <form id="form1" runat="server"> <p>one</p> <div><p style="display:none">two</p></div> <p>three</p> <br /> <div> <input type="text" />&nbsp;&nbsp;<input id="chkTest" type="checkbox" checked="checked" />&nbsp;&nbsp;<input id="btnTest" type="button" value="button" /></div> </form> 2、jQuery(html,[ownerDocument])根据提供的原始HTML标记字符串,动态创建由 jQuery 对象包装的DOM元素。 可以传递一个手写的HTML字符串,或者由某些模板引擎或插件创建的字符串,也可以是通过 AJAX 加载过来的字符串。但是在创建input 元素的时会有限制,可以参考第二个示例。当然这个字符串可以包含斜杠 (比如一个图像地址),还有反斜杠。 当创建单个元素时,请使用闭合标签或 XHTML 格式。例如,创建一个span,可以用 $("<span/>") 或 $("<span></span>") ,但不推荐 $("<span>")。在jQuery 中,这个语法等同于$(document.createElement("span")) 。 返回值 jQuery参数 html (String) : 用于动态创建DOM元素的HTML标记字符串 ownerDocument (Document) : 可选,创建DOM元素所在的文档 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(document).ready(function() { $("<div><p>This is a jQuery test.</p></div>").appendTo("body"); //创建一个input元素 if (!$.browser.msie) { $("<input>").attr("type", "checkbox").appendTo("body"); // 在 IE 中无效: } else { // 所有浏览器中有效 $("<input type='checkbox'>").appendTo("body"); } }) 3、jQuery(elements) 将一个或多个DOM元素转化为jQuery对象。 这个函数也可以接收XML文档和Window对象(虽然它们不是DOM元素)作为有效的参数。返回值 jQuery参数 elements (Element, Array<Element>) : 用于封装成jQuery对象的DOM元素 示例: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(document).ready(function() { $(document.body).css("background", "cef"); //设置页面背景色 $(document.forms[0].elements).hide(); //隐藏一个表单中的所有元素}) 4、jQuery(callback) $(document).ready()的简写。 允许绑定一个在DOM文档载入完成后执行的函数。这个函数的作用如同$(document).ready()一样,只不过用这个函数时,需要把页面中所有需要在DOM 加载完成时执行的$()操作符都包装到其中来。可以在一个页面中使用任意多个$(document).ready事件。返回值 jQuery参数 callback (Function) : 当DOM加载完成后要执行的函数 示例: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { alert("文档加载就绪"); // 文档加载就绪}); 注意: 使用 $(document).ready() 的简写,同时内部的 jQuery 代码依然使用 $ 作为别名,而不管全局的 $ 为何。 jQuery 代码: jQuery(function($) { // 可以在这里继续使用$作为别名... }); 二、对象访问1、each(callback) 以每一个匹配的元素作为上下文来执行一个函数。 意味着,每次执行传递进来的函数时,函数中的this关键字都指向一个不同的DOM元素(每次都是一个不同的匹配元素)。而且,在每次执行函数时,都会给函数传递一个表示作为执行环境的元素在匹配的元素集合中所处位置的数字值作为参数(从零开始的整形)。 返回 'false' 将停止循环 (就像在普通的循环中使用 'break')。返回 'true' 跳至下一个循环(就像在普通的循环中使用'continue')。返回值 jQuery参数 callback (Function) : 对于每个匹配的元素所要执行的函数 示例: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$("img").each(function(i) {this.src = "images/test" + (i + 1).toString() + ".jpg"; // 结果: <img src="images/test1.jpg" />}); 如果你想得到 jQuery对象,可以使用 $(this) 函数。 jQuery 代码: $("img").each(function(){ $(this).toggleClass("example"); }); 下面再实现一个使用 'return' 来提前跳出 each() 循环: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$("button").click(function() { $("div").each(function(index, domEle) { // domEle == this $(domEle).css("backgroundColor", "yellow"); if ($(this).is("#stop")) { $("span").text("Stopped at div index #" + index); return false; //使用 'return'来提前跳出each()循环 } }); }); 文档片段: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><button>改变背景色</button><span></span> <div></div> <div></div><div></div> <div></div><div id="stop">在这里停止</div> <div></div><div></div><div></div> 3、size()和lengthjQuery 对象中元素的个数。 这个函数的返回值与 jQuery 对象的length属性一致。返回值 Number参数 没有参数 示例: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { var len = $("img").size(); //计算文档中所有图片数量 alert(len); alert($("img").length == len); //与jQuery对象的length属性一致}) 4、selector jQuery 1.3新增。返回传给jQuery()的原始选择器。 换句话说,就是返回你用什么选择器来找到这个元素的。可以与context一起使用,用于精确检测选择器查询情况。这两个属性对插件开发人员很有用。返回值 String参数 没有参数 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { //确定查询的选择器 $("ul") .append("<li>" + $("ul").selector + "</li>") .append("<li>" + $("ul li").selector + "</li>") .append("<li>" + $("div#foo ul:not([class])").selector + "</li>"); }) 5、contextjQuery 1.3新增。返回传给jQuery()的原始的DOM节点内容,即jQuery()的第二个参数。如果没有指定,那么context指向当前的文档(document)。 可以与selector一起使用,用于精确检测选择器查询情况。这两个属性对插件开发人员很有用。返回值 HTMLElement 示例 Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { //检测使用的文档内容 $("ul") .append("<li>" + $("ul").context + "</li>") .append("<li>" + $("ul").context.nodeName + "</li>") .append("<li>" + $("ul", document.body).context.nodeName + "</li>"); }) 6、get()取得所有匹配的 DOM 元素集合。 这是取得所有匹配元素的一种向后兼容的方式(不同于jQuery对象,而实际上是元素数组)。如果你想要直接操作 DOM 对象而不是 jQuery 对象,这个函数非常有用。返回值 Array<Element>参数 没有参数 示例: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { $("img").get().reverse(); //选择文档中所有图像作为元素数组,并用数组内建的reverse方法将数组反转}) 文档片段: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> <form id="form1" runat="server" > <img src="images/test1.jpg"/><br /> <img src="images/test2.jpg"/> </form> 7、get(index) 取得其中一个匹配的元素。 index表示取得第几个匹配的元素。 这能够让你选择一个实际的DOM 元素并且对他直接操作,而不是通过 jQuery 函数。$(this).get(0)与$(this)[0]等价。返回值 Element参数 index (Number) :取得第 index 个位置上的元素 示例: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { var imgSrc = $("img").get(0).src; //取图片对应路径 alert(imgSrc); }) 8、index(subject) 搜索与参数表示的对象匹配的元素,并返回相应元素的索引值。 如果找到了匹配的元素,从1开始返回;如果没有找到匹配的元素,返回-1。返回值 Number参数 subject (Element) : 要搜索的对象 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { //返回对应ID值的元素的索引值 alert($("div").index($('#f'))); // -1 alert($("div").index($('#foobar'))); // 1 alert($("div").index($('#fo'))); // 2 alert($("div").index($('#foo'))); // 3}) 文档片段: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><div id="foobar"><div id="fo"></div><div id="foo"></div></div> 三、数据缓存1、data(name)返回元素上储存的相应名字的数据,可以用data(name, value)来设定。 如果jQuery集合指向多个元素,那将只返回第一个元素的对应数据。 这个函数可以用于在一个元素上存取数据而避免了循环引用的风险。jQuery.data是1.2.3版的新功能。可以在很多地方使用这个函数,另外jQuery UI里经常使用这个函数。返回值 Any参数 name (String) :存储的数据名 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { //在一个div上存取数据 $("div").data("blah"); // undefined alert($("div").data("blah")); $("div").data("blah", "hello world"); // blah设置为hello world $("div").data("blah"); // hello $("div").data("blah", 123); // 设置为123 $("div").data("blah"); // 123 alert($("div").data("blah")); $("div").removeData("blah"); //移除blah $("div").data("blah"); // undefined //在一个div上存取名/值对数据 $("div").data("test", { first: 456, last: "jeff wong!" }); alert($("div").data("test").first); //456; alert($("div").data("test").last); //jeff wong }) 2、removeData(name)在元素上移除存放的数据 与$(...).data(name, value)函数作用相反返回值 jQuery参数 name (String) :存储的数据名 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$(function() { $("div").data("test", { first: 456, last: "jeff wong!" }); alert($("div").data("test").first); //456; alert($("div").data("test").last); //jeff wong $("div").removeData("test"); //移除test alert($("div").data("test")); //undefined;}) 3、queue([name]) 返回指向第一个匹配元素的队列(将是一个函数数组)返回值 Array<Function>参数 name (String) :队列名,默认为fx 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//通过设定队列数组来删除动画队列$("#btnShow").click(function() { var n = $("div").queue("fx"); $("span").text("Queue length is: " + n.length); });function runIt() { $("div").show("slow"); $("div").animate({ left: '+=200' }, 2000); $("div").slideToggle(1000); $("div").slideToggle("fast"); $("div").animate({ left: '-=200' }, 1500); $("div").hide("slow"); $("div").show(1200); $("div").slideUp("normal", runIt); } runIt(); 文档片段: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> <title></title> <style> div { margin: 3px; width: 40px; height: 40px; position: absolute; left: 0px; top: 30px; background: green; display: none; } div.newcolor { background: blue; } span { color: red; } </style> <script src="js/jquery-1.3.1.js" type="text/javascript"></script></head><body> <form id="form1" runat="server"> <button id="btnShow" type="button"> Show Length of Queue</button> <span style="color:Red"></span> </form> <script src="js/jQTest.js" type="text/javascript"></script></body></html> 4、queue([name],callback) 在匹配的元素的队列最后添加一个函数返回值 jQuery参数 name (String) :队列名,默认为fx callback (Function) : 要添加进队列的函数 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//插入一个自定义函数 如果函数执行后要继续队列,则执行 jQuery(this).dequeue();jQuery(document).ready(function() { $(document.body).click(function() { $("div").show("slow"); $("div").animate({ left: '+=200' }, 2000); $("div").queue(function() { $(this).addClass("newcolor"); $(this).dequeue(); }); $("div").animate({ left: '-=200' }, 500); $("div").queue(function() { $(this).removeClass("newcolor"); $(this).dequeue(); }); $("div").slideUp(); }); }); 文档片段: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><html><head> <style> div { margin:3px; width:40px; height:40px; position:absolute; left:0px; top:30px; background:green; display:none; } div.newcolor { background:blue; }</style></head><body> Click here <div></div></body></html> 5、queue([name],queue)将匹配元素的队列用新的一个队列来代替(函数数组).返回值 jQuery参数 name (String) :队列名,默认为fx queue (Array<Function>) : 用于替换的队列。所有函数都有同一个参数,这个值与queue(callback)相同 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//通过设定队列数组来删除动画队列 jQuery(document).ready(function() { $("#start").click(function() { $("div").show("slow"); $("div").animate({ left: '+=200' }, 5000); $("div").queue(function() { $(this).addClass("newcolor"); $(this).dequeue(); }); $("div").animate({ left: '-=200' }, 1500); $("div").queue(function() { $(this).removeClass("newcolor"); $(this).dequeue(); }); $("div").slideUp(); }); $("#stop").click(function() { $("div").queue("fx", []); //通过设定队列数组来删除动画队列 $("div").stop(); }); }); 文档片段: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><html><head> <style> div { margin:3px; width:40px; height:40px; position:absolute; left:0px; top:30px; background:green; display:none; } div.newcolor { background:blue; } </style></head><body> <button id="start">Start</button> <button id="stop">Stop</button> <div></div></body></html> 6、dequeue([name])从队列最前端移除一个队列函数,并执行它。返回值 jQuery参数 name (String) :队列名,默认为fx 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->$("button").click(function() { $("div").animate({ left: '+=200px' }, 2000); $("div").animate({ top: '0px' }, 600); $("div").queue(function() { $(this).toggleClass("red"); $(this).dequeue(); //用dequeue来结束自定义队列函数,并让队列继续进行下去。 }); $("div").animate({ left: '10px', top: '30px' }, 700); }); 文档片段: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><html><head> <style> div { margin:3px; width:50px; position:absolute; height:50px; left:10px; top:30px; background-color:yellow; } div.red { background-color:red; } </style></head><body> <button>Start</button> <div></div></body></html> ps:上面的一些示例用到了一些jquery特效函数,因为本篇主要阐述核心函数,对于单个特效函数这里不再详细说明使用细节。 四、插件机制 1、jQuery.fn.extend(object)扩展 jQuery 元素集来提供新的方法(通常用来制作插件)返回值 jQuery参数 object (Object) :用来扩充 jQuery 对象。 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//增加两个插件方法。jQuery.fn.extend({ check: function() { return this.each(function() { this.checked = true; }); }, uncheck: function() { return this.each(function() { this.checked = false; }); } }); $("input[type=checkbox]").check(); $("input[type=radio]").uncheck(); 文档片段: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> <input type="checkbox" /> <input type="radio" checked="checked"/> 2、jQuery.extend(object)扩展jQuery对象本身,用来在jQuery命名空间上增加新函数返回值 jQuery参数 object (Object) :用来扩充 jQuery 对象。 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//在jQuery命名空间上增加两个函数jQuery.extend({ min: function(a, b) { return a < b ? a : b; }, max: function(a, b) { return a > b ? a : b; } }); alert(jQuery.min(2, 3)); // => 2alert(jQuery.max(4, 5)); // => 5 五、多库共存1、jQuery.noConflict() 运行这个函数将变量$的控制权让渡给第一个实现它的那个库。这有助于确保jQuery不会与其他库的$对象发生冲突。在运行这个函数后,就只能使用jQuery变量访问jQuery对象。 例如,在要用到$("div p")的地方,就必须换成jQuery("div p")。注意:这个函数必须在导入jQuery文件之后,并且在导入另一个导致冲突的库之前使用。当然也应当在其他冲突的库被使用之前,除非jQuery是最后一个导入的。返回值 jQuery 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//1、将$引用的对象映射回原始的对象。jQuery.noConflict();// 使用 jQueryjQuery("div p").hide();// 使用其他库的 $()$("content").style.display = 'none';//2、恢复使用别名$,然后创建并执行一个函数,在这个函数的作用域中仍然将$作为jQuery的别名来使用。//在这个函数中,原来的$对象是无效的。这个函数对于大多数不依赖于其他库的插件都十分有效。 jQuery.noConflict(); (function($) { $(function() { // 使用 $ 作为 jQuery 别名的代码 }); })(jQuery);// 其他用 $ 作为别名的库的代码//3、创建一个新的别名用以在接下来的库中使用jQuery对象。var j = jQuery.noConflict();// 基于 jQuery 的代码j("div p").hide();// 基于其他库的 $() 代码$("content").style.display = 'none'; 2、jQuery.noConflict(extreme) 将$和jQuery的控制权都交还给原来的库(用之前请考虑清楚!) 这是相对于简单的 noConflict 方法更极端的版本,因为这将完全重新定义jQuery。这通常用于一种极端的情况,比如你想要将jQuery嵌入一个高度冲突的环境。注意:调用此方法后极有可能导致插件失效。返回值 jQuery参数 extreme (Boolean) : 传入 true 来允许彻底将jQuery变量还原示例: 示例: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->//完全将 jQuery 移到一个新的命名空间。 var dom = {}; dom.query = jQuery.noConflict(true); // 新jQuery的代码dom.query("div p").hide();// 另一个库 $() 的代码$("content").style.display = 'none';// 另一个版本 jQuery 的代码jQuery("div > p").hide(); 好了,核心部分就介绍到这里。现在回头来看,jQuery函数封装的方式比直接调用js和dom的各种函数,真是方便直接多了。 振作精神,下一篇接着把jQuery的选择器和属性介绍一下。函数很多,现在记不住就记不住算了,多写多用熟练了就好。以后用到的时候,可以直接当自己的在线参考书。希望对你也有帮助。 原文地址: 原文地址:http://www.cnblogs.com/wjfluisfigo/archive/2009/08/08/1530624.html 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/08/08/1541992.html,如需转载请自行联系原作者
表头定义说明: 表头定义方法:相邻父列头之间用'#'分隔,上级行与下级行用空格(' ')分隔,相邻未级子列头用逗号分隔(','). 表头定义示例: A.两层 烟叶等级#级别#保山市 保山,小计#楚雄州 姚安,小计#大理州 宾川,大理,小计#红河州 建水,泸西,弥勒,石屏,小计#昆明市 富民,禄劝,小计#丽江市 华坪,宁蒗,小计#临沧市 沧源,凤庆,耿马,临翔,双江,永德,云县,镇康,小计#普洱市 景东,思茅,镇沅,小计#曲靖市 富源,陆良,罗平,师宗,宣威,小计#文山州 文山,小计#玉溪市 玉溪,小计#总计 B.三层 等级#级别#上期结存 件数,重量,比例#本期调入 收购调入 件数,重量,比例#本期发出 车间投料 件数,重量,比例#本期发出 产品外销百分比 件数,重量,比例#平均值 调用说明:使用时在GridView的RowCreated事件中加入下面代码调用 Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> if (e.Row.RowType == DataControlRowType.Header) { DynamicTHeaderHepler dHelper = new DynamicTHeaderHepler(); string header = "等级#级别#上期结存 件数,重量,比例#本期调入 收购调入 件数,重量,比例#本期发出 车间投料 件数,重量," + "比例#本期发出 产品外销百分比 件数,重量,比例#平均值"; dHelper.SplitTableHeader(e.Row, header); }表头生成类:////***********************************************************************// Created: 2007-10-29 Author: ruijc// File: DynamicTHeaderHepler.cs// Description: 动态生成复合表头帮助类// 相邻父列头之间用'#'分隔,父列头与子列头用空格(' ')分隔,相邻子列头用逗号分隔(',').//***********************************************************************using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Collections.Generic;using System.Collections;public class DynamicTHeaderHepler{ public DynamicTHeaderHepler() { // // TODO: Add constructor logic here // } /**//**//**//// <summary> /// 重写表头 /// </summary> /// <param name="targetHeader">目标表头</param> /// <param name="newHeaderNames">新表头</param> /// <remarks> /// 等级#级别#上期结存 件数,重量,比例#本期调入 收购调入 件数,重量,比例#本期发出 车间投料 件数,重量, /// 比例#本期发出 产品外销百分比 件数,重量,比例#平均值 /// </remarks> public void SplitTableHeader(GridViewRow targetHeader, string newHeaderNames){ TableCellCollection tcl = targetHeader.Cells;//获得表头元素的实例 tcl.Clear();//清除元素 int row = GetRowCount(newHeaderNames); int col = GetColCount(newHeaderNames); string[,] nameList = ConvertList(newHeaderNames,row,col); int RowSpan = 0; int ColSpan = 0; for (int k = 0; k < row; k++) { string LastFName = ""; for (int i = 0; i < col; i++) { if (LastFName == nameList[i, k] && k!=row-1) { LastFName = nameList[i, k]; continue; } else { LastFName = nameList[i, k]; } int bFlag=IsVisible(nameList, k, i, LastFName); switch (bFlag) { case 0: break; case 1: RowSpan = GetSpanRowCount(nameList,row, k, i); ColSpan = GetSpanColCount(nameList,row,col, k, i); tcl.Add(new TableHeaderCell());//添加表头控件 tcl[tcl.Count - 1].RowSpan = RowSpan; tcl[tcl.Count - 1].ColumnSpan = ColSpan; tcl[tcl.Count - 1].HorizontalAlign = HorizontalAlign.Center; tcl[tcl.Count - 1].Text = LastFName; break; case -1: string[] EndColName = LastFName.Split(new char[] { ',' }); foreach(string eName in EndColName){ tcl.Add(new TableHeaderCell());//添加表头控件 tcl[tcl.Count - 1].HorizontalAlign = HorizontalAlign.Center; tcl[tcl.Count - 1].Text = eName; } break; } } if (k != row-1) {//不是起始行,加入新行标签 tcl[tcl.Count - 1].Text = tcl[tcl.Count - 1].Text+"</th></tr><tr>"; } } } /**//**//**//// <summary> /// 如果上一行已经输出和当前内容相同的列头,则不显示 /// </summary> /// <param name="ColumnList">表头集合</param> /// <param name="rowIndex">行索引</param> /// <param name="colIndex">列索引</param> /// <returns>1:显示,-1:含','分隔符,0:不显示</returns> private int IsVisible(string[,] ColumnList,int rowIndex, int colIndex,string CurrName) { if (rowIndex!=0){ if (ColumnList[colIndex,rowIndex-1]==CurrName){ return 0; }else{ if (ColumnList[colIndex, rowIndex].Contains(",")) { return -1; }else{ return 1; } } } return 1; } /**//**//**//// <summary> /// 取得和当前索引行及列对应的下级的内容所跨的行数 /// </summary> /// <param name="ColumnList">表头集合</param> /// <param name="row">行数</param> /// <param name="rowIndex">行索引</param> /// <param name="colIndex">列索引</param> /// <returns>行数</returns> private int GetSpanRowCount(string[,] ColumnList, int row,int rowIndex, int colIndex) { string LastName = ""; int RowSpan = 1; for (int k = rowIndex; k < row; k++) { if (ColumnList[colIndex,k]==LastName){ RowSpan++; }else{ LastName = ColumnList[colIndex, k]; } } return RowSpan; } /**//**//**//// <summary> /// 取得和当前索引行及列对应的下级的内容所跨的列数 /// </summary> /// <param name="ColumnList">表头集合</param> /// <param name="row">行数</param> /// <param name="col">列数</param> /// <param name="rowIndex">行索引</param> /// <param name="colIndex">列索引</param> /// <returns>列数</returns> private int GetSpanColCount(string[,] ColumnList,int row, int col,int rowIndex, int colIndex){ string LastName = ColumnList[colIndex, rowIndex] ; int ColSpan = ColumnList[colIndex, row-1].Split(new char[] { ',' }).Length; ColSpan = ColSpan == 1 ? 0 : ColSpan; for(int i=colIndex+1;i<col;i++){ if (ColumnList[i, rowIndex] == LastName) { ColSpan += ColumnList[i, row - 1].Split(new char[] { ',' }).Length; } else { LastName = ColumnList[i, rowIndex]; break; } } return ColSpan; } /**//**//**//// <summary> /// 将已定义的表头保存到数组 /// </summary> /// <param name="newHeaders">新表头</param> /// <param name="row">行数</param> /// <param name="col">列数</param> /// <returns>表头数组</returns> private string[,] ConvertList(string newHeaders, int row, int col) { string[] ColumnNames = newHeaders.Split(new char[] { '#' }); string[,] news = new string[col, row]; string Name = ""; for (int i = 0; i < col; i++) { string[] CurrColNames = ColumnNames[i].ToString().Split(new char[] { ' ' }); for (int k = 0; k < row; k++) { if (CurrColNames.Length - 1 >= k) { if (CurrColNames[k].Contains(",")) { if (CurrColNames.Length != row) { if (Name == "") { news[i, k] = news[i, k - 1]; Name = CurrColNames[k].ToString(); } else { news[i, k + 1] = Name; Name = ""; } }else{ news[i, k] = CurrColNames[k].ToString(); } }else{ news[i, k] = CurrColNames[k].ToString(); } }else{ if (Name == "") { news[i, k] = news[i, k - 1]; }else{ news[i, k] = Name; Name = ""; } } } } return news; } /**//**//**//// <summary> /// 取得复合表头的行数 /// </summary> /// <param name="newHeaders">新表头</param> /// <returns>行数</returns> private int GetRowCount(string newHeaders) { string[] ColumnNames = newHeaders.Split(new char[] { '#' }); int Count = 0; foreach(string name in ColumnNames){ int TempCount = name.Split(new char[] { ' ' }).Length; if (TempCount > Count) Count = TempCount; } return Count; } /**//**//**//// <summary> /// 取得复合表头的列数 /// </summary> /// <param name="newHeaders">新表头</param> /// <returns>列数</returns> private int GetColCount(string newHeaders){ return newHeaders.Split(new char[] { '#' }).Length; } } 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/aoeagle/archive/2008/02/20/2108567.aspx 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/10/11/1580734.html,如需转载请自行联系原作者
1.介绍 在SharePoint中你站点中的所有文件都是存在于内容数据库中的,比如列表中的Item,文档库中的文档。有很多时候在文档库中我们很多很大的文件,这个时候就会导致我们的内容数据库变得很大,而且增长的速度很快,我们这个时候希望这些文档能存在文件系统中,使用RBS就可以做到这点。 SQL Server 2008推出了两个用于存储BLOB数据的新功能: · FILESTREAM:是一个你可以在varbinary字段上设置的属性,以便数据存储在文件系统中(因此受益于它的快速流式功能和存储能力)但是直接在数据库的上下文中管理和访问。: · 远程BLOB存储:一个客户端应用程序编程接口(API),它降低了建立依赖于一个用于BLOB的外部存储和一个用于关系数据的数据库的应用程序的复杂性。 关于RBS这里有一篇PPThttp://www.docin.com/p-37773363.html。下面是具体配置的截图,请大家参考,如果对你有帮助请推荐下。 2.Enable FILESTREAM and Provision the RBS Data Store 打开sql server,查询分析器中执行下面sql语句 通过下面powershell得到content db名字 执行下面脚本 执行下面脚本 执行下面脚本 ,将RBSFilestream 添加到RBSFilestreamProvider 文件组中. use WSS_Content_e58f8446cde041318a305696f040d32a alter database WSS_Content_e58f8446cde041318a305696f040d32a add file (name = RBSFilestreamFile, filename = 'c:\Blobstore') to filegroup RBSFilestreamProvider 下面设置为Full Access 3.Install the RBS FILESTREAM Provider Powershell中执行下面命令 PS C:\Users\Administrator> cd\ PS C:\> msiexec /qn /lvx* rbs_install_log.txt /i C:\ITPRO\RBS\RBSInstall\RBS.msi TRUSTSERVERCERTIFICATE=true FILEGROUP=PRIMARY DBNAME="WSS_Content_e58f8446cde04 1318a305696f040d32a" DBINSTANCE="DEMO2010A" FILESTREAMFILEGROUP=RBSFilestreamPro vider FILESTREAMSTORENAME=FilestreamProvider_1 打开c盘 rbs.install_log.txt文件,大小有1300kb左右,看到下面信息说明安装成功 4.Enable the Provider on a Content Database This command($RBSS.Enable()) enables the RBS FILESTREAM Provider. Note the Default configuration is used for the ConsoleLog, FileLog, CircularLog, EventViewerLog and DatabaseTableLog. 执行下面脚本 $rbss.SetActiveProviderName($rbss.GetProviderNames()[0]) 再次查看如下: 我们去文档库中上传一个文档 在c盘我们设置的位置,会有一个和你上传文件size大小一样的文件,如下图 5.Configure the RBS FILESTREAM Provider 使用这种方式有性能影响的,所以小的文件我们没有必要使用该方式,直接存储到sharepoint content db中就可以,我们可以进行下面配置,设置为1Mb: 本文转自Justin博客园博客,原文链接:http://www.cnblogs.com/carysun/archive/2010/12/28/mossrbs.html,如需转载请自行联系原作者
问题:阿里云服务器,专有网络,web设置完毕,在服务器中localhost能够访问,并且关闭防火墙,但是使用公网ip无法访问。 解决:找到本实例安全组,配置规则,按照要求填入80或其他端口。配置完成后,能使用公网ip加端口访问 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7447037.html,如需转载请自行联系原作者
xcuserstate 每次并没有改什么东西,只是随便点了几下就会出现的未暂存文件,可以对其停止追踪! 右键,停止追踪,提交,推送。以后就不会再有这个讨厌的文件出现了! 还没有提交就拉代码的囧境 有的时候忘了拉取代码就开始提交,往往这时候就会出现提交成功,但无法推送的报错情况: 而且这时候的状态就是既无法提交又不能拉取代码的窘境,就连贮藏也不行。 搞了很久,研究出两个办法: 方法一:使用reset+贮藏 首先,将本地的develop分支reset到之前的版本: reset develop to this commit 选择软合并或者是默认的混合合并【千万不要选择硬合并!不然之前码的代码都没了】 然后就回到了初始状态: 这时候再暂存+贮藏一下,拉取代码,应用贮藏,解决冲突就可以了~ 方法二:建立一个新的分支 右键新建一个test分支: 然后checkout(检出,其实就是双击)到原来的分支: 将原来的develop分支reset到初始状态(这个时候要用硬合并,因为你的代码已经保存到test分支里面了所以不用担心) 这个时候终于可以拉取远端了! 然后再把test分支合并到develop分支就可以了~ 其实总结一下这两个方法的原理是一样的,都是先把自己的代码存到一边,然后再拉取远端代码,再合并。如果中间遇到不能拉取的情况,一定是xcuserstate文件在作怪!嗯! 分类: 项目管理,项目有关 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7212290.html,如需转载请自行联系原作者
1、swift中用let关键字类定义常量,用var关键字来定义变量。 2、swift语句的结尾不需要再带逗号,系统在运行程序时自动会帮你添加上 3、一个变量或常量必须与赋值时拥有相同的类型。 4、如果初始化值没有提供足够的信息(或没有初始化值),可以在变量名后写类型,并用冒号分隔。 5、如果第一次赋初值变量的类型不确定,它可以根据再一次赋值时来确定当前变量的数据类型。 命名规范: 常用数据类型: 类型别名: 新类型元组 : 字符和字符串: 具体举例如下: 用var定义隐式变量,开始赋初值不能确定类型,等第二次赋值后,类型即可确定,此时再赋值类型需要一样 //定义变量用var //不用加类型 //语句后面不用加分号 var str = "Hello, playground" println(str) //"Hello,palyground" var myVariable = 40 myVariable = 55 myVariable = 56.7 //myVariable = "hello world" //error,类型不匹配 用let定义常量,赋初值后,不允许再赋值 //定义常量用let let myConstant = 40 //myConstant = 55 //error,常量不可再赋值 显示的定义变量或常量的类型,变量或常量的后面跟上": 类型" //显示声明变量或常量的类型 var num:Int = 30 let num2:Double = 30.5 其他类型转化为字符串类型,采用String(...)的方式强制转换为字符串 //其他类型转换为字符串 let label = "The width is " let width = 94 let widthLabel = label + String(width) //"The width is 94" 字符串可以直接用"+"号连接成一个新的字符串 //字符串的连接直接用加号var str = "hello" var str2 = "good morning" println(str+" "+str2) //"hello good morning" 在字符串中用" \(...) "这种方式可以用来格式化字符串 //格式化字符串var num = 2.0 let str3 = "good morning \(num)" //"good mornig 2.0" 字符串其他运算: 定义数组,用[ ]括号,通过索引获取数组中的值 //定义数组,用[] var arr = [1,2,3,4] arr[1] = 6 println("\(arr)") //"6" 数组其他运算方式: 添加元素 数组元素个数和容量 遍历元素: 删除元素: 设置数组多个元素: 定义一个字典,也用[ ]括号,没有显示指定字典类型时,键和值类型要保持一致,通过键key获取对应的值。如果没有找到,就会将其新添加进字典中 //定义一个字典,也用[],键和值类型需要保持一致 var dic = ["name":"Tom","age":"20"] dic["age"] = "22" println("\(dic)") //["name":"Tom","age":"22"]dic["sex"]= "M"println("\(dic)") //["name":"Tom","age":"22","sex":"M"] 字典其他运算方式: 遍历字典: 删除元素: 定义未知类型的空数组或空字典 //创建空数组 var arr3 = [] //创建空字典 var dic3 = [:] 定义指定类型的空数组或空字典 //创建空的字符串数组 var emptyArray = [String]() emptyArray = ["Tom"]//创建空的泛型字典 var emptyDictionary = Dictionary<String,Float>() emptyDictionary = ["tom":20,"jobs":23] 创建元组,可以同时存放任意类型的数据 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4902736.html,如需转载请自行联系原作者
RabbitMQ无疑是目前最流行的消息队列之一,对各种语言环境的支持也很丰富,作为一个.NET developer有必要学习和了解这一工具。消息队列的使用场景大概有3种: 1、系统集成,分布式系统的设计。各种子系统通过消息来对接,这种解决方案也逐步发展成一种架构风格,即“通过消息传递的架构”。 2、当系统中的同步处理方式严重影响了吞吐量,比如日志记录。假如需要记录系统中所有的用户行为日志,如果通过同步的方式记录日志势必会影响系统的响应速度,当我们将日志消息发送到消息队列,记录日志的子系统就会通过异步的方式去消费日志消息。 3、系统的高可用性,比如电商的秒杀场景。当某一时刻应用服务器或数据库服务器收到大量请求,将会出现系统宕机。如果能够将请求转发到消息队列,再由服务器去消费这些消息将会使得请求变得平稳,提高系统的可用性。 一、开始使用RabbitMQ RabbitMQ官网提供了详细的安装步骤,另外官网还提供了RabbitMQ在六种场景的使用教程。其中教程1、3、6将覆盖99%的使用场景,所以正常来说只需要搞清楚这3个教程即可快速上手。 二、简单分析 我们以官方提供的教程1做个简单梳理:该教程展示了Producer如何向一个消息队列(message queue)发送一个消息(message),消息消费者(Consumer)收到该消息后消费该消息。 1、producer端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 var factory = new ConnectionFactory() { HostName = "localhost" }; using (var connection = factory.CreateConnection()) { while (Console.ReadLine() != null) { using (var channel = connection.CreateModel()) { //创建一个名叫"hello"的消息队列 channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null); var message = "Hello World!"; var body = Encoding.UTF8.GetBytes(message); //向该消息队列发送消息message channel.BasicPublish(exchange: "", routingKey: "hello", basicProperties: null, body: body); Console.WriteLine(" [x] Sent {0}", message); } } } 该段代码非常简单,几乎到了无法精简的地步:创建了一个信道(channel)->创建一个队列->向该队列发送消息。 2、Consumer端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var factory = new ConnectionFactory() { HostName = "localhost" }; using (var connection = factory.CreateConnection()) { using (var channel = connection.CreateModel()) { //创建一个名为"hello"的队列,防止producer端没有创建该队列 channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null); //回调,当consumer收到消息后会执行该函数 var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body); Console.WriteLine(" [x] Received {0}", message); }; //消费队列"hello"中的消息 channel.BasicConsume(queue: "hello", noAck: true, consumer: consumer); Console.WriteLine(" Press [enter] to exit."); Console.ReadLine(); } } 该段代码可以理解为:创建信道->创建队列->定义回调函数->消费消息。 该实例描述了Send/Receive模式,可以简单理解为1(producer) VS 1(consumer)的场景; 实例3则描述了Publish/Subscriber模式,即1(producer) VS 多个(consumer); 在以上两个示例中,producer只需要发送消息即可,并不关心consumer的返回结果。实例6则描述了一个RPC调用场景,producer发送消息后还要接收consumer的返回结果,这一场景看起来跟使用消息队列的目的有点相悖。因为使用消息队列的目的之一就是要异步,但是这一场景似乎又将异步变成了同步,不过这一场景也很有用,比如一个用户操作产生了一个消息,应用服务收到该消息后执行了一些逻辑并使得数据库发生了变化,UI会一直等待应用服务的返回结果才刷新页面。 三、 发现抽象 我桌子上放着一本RabbitMQ in Action,另外官网提供的文档也很详细,我感觉在一个月内我就能精通RabbitMQ,到时候简历上又可以写上“精通…”,感觉有点小得意呢... ,但是我知道这并不是使用RabbitMQ的最佳方式。 我们知道合理的抽象可以帮我们隐藏掉一些技术细节,让我们将重心放在核心业务上,比如一个人问你:“大雁塔如何走?”你的回答可能是“小寨往东,一直走两站,右手边”,如果你回答:“右转45度,向前走100米,再转90度…”,对方就会迷失在这些细节中。 消息队列的使用过程中实际隐藏着一种抽象——服务总线(Service Bus)。 我们在回头看第一个例子,这个例子隐含的业务是:ClientA发送一个指令,ClientB收到该指令后做出反应。如果是这样,我们为什么要关心如何创建channel,如何创建一个queue? 我仅仅是要发送一个消息而已。另外这个例子写的其实不够健壮: 没有重试机制:如果ClientB第一次没有执行成功如何对该消息处理? 没有错误处理机制:如果ClientB在重试了N次之后还是异常如何处理该消息? 没有熔断机制; 如何对ClientA做一个schedule(计划安排),比如定时发送等; 没有消息审计机制; 无法对消息的各个状态做追踪; 事物处理等。 服务总线正是这种场景的抽象,并且为我们提供了这些机制,让我们赶快来看个究竟吧。 四、初识MassTransit MassTransit是.NET平台下的一款开源免费的ESB产品,官网:http://masstransit-project.com/,GitHub 700 star,500 Fork,类似的产品还有NServiceBus,之所以要选用MassTransit是因为他要比NServiceBus轻量级,另外在MassTransit开发之初就选用了RabbitMQ作为消息传输组建;同时我想拿他跟NServiceBus做个比较,看看他们到底有哪些侧重点。 1、新建控制台应用程序:Masstransit.RabbitMQ.GreetingClient 使用MassTransit可以从Nuget中安装: 1 Install-Package MassTransit.RabbitMQ 2、创建服务总线,发送一个命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static void Main(string[] args) { Console.WriteLine("Press 'Enter' to send a message.To exit, Ctrl + C"); var bus = BusCreator.CreateBus(); var sendToUri = new Uri($"{RabbitMqConstants.RabbitMqUri}{RabbitMqConstants.GreetingQueue}"); while (Console.ReadLine()!=null) { Task.Run(() => SendCommand(bus, sendToUri)).Wait(); } Console.ReadLine(); } private static async void SendCommand(IBusControl bus,Uri sendToUri) { var endPoint =await bus.GetSendEndpoint(sendToUri); var command = new GreetingCommand() { Id = Guid.NewGuid(), DateTime = DateTime.Now }; await endPoint.Send(command); Console.WriteLine($"send command:id={command.Id},{command.DateTime}"); } 这一段代码隐藏了众多关于消息队列的细节,将我们的注意力集中在发送消息上,同时ServiceBus提供的API也更接近业务,我们虽然发送的是一个消息,但是在这种场景下体现出来是一个命令,Send(command)这一API描述了我们的意图。 3、服务端接收这一命令 新建一个命令台控制程序:Masstransit.RabbitMQ.GreetingServer 1 2 3 4 5 6 7 8 var bus = BusCreator.CreateBus((cfg, host) => { cfg.ReceiveEndpoint(host, RabbitMqConstants.GreetingQueue, e => { e.Consumer<GreetingConsumer>(); }); }); 这一代码可以理解为服务端在监听消息,我们在服务端注册了一个名为“GreetingConsumer”的消费者,GreetingConsumer的定义: 1 2 3 4 5 6 7 8 public class GreetingConsumer :IConsumer<GreetingCommand> { public async Task Consume(ConsumeContext<GreetingCommand> context) { await Console.Out.WriteLineAsync($"receive greeting commmand: {context.Message.Id},{context.Message.DateTime}"); } } 该consumer可以消费类型为GreetingCommand的消息。这一实例几乎隐藏了有关RabbitMQ的技术细节,将代码中心放在了业务中,将这两个控制台应用跑起来试试: 五、实现Publish/Subscribe模式 发布/订阅模式使得基于消息传递的软件架构成为可能,这一能力表现为ClientA发送消息X,ClientB和ClientC都可以订阅消息X。 1、我们在上面的例子中改造一下,当GreetingConsumer收到GreetingCommand后发送一个GreetingEvent: 1 2 3 4 5 6 7 var greetingEvent = new GreetingEvent() { Id = context.Message.Id, DateTime = DateTime.Now }; await context.Publish(greetingEvent); 2、新建控制台程序Masstransit.RabbitMQ.GreetingEvent.SubscriberA用来订阅GreetingEvent消息: 1 2 3 4 5 6 7 8 9 var bus = BusCreator.CreateBus((cfg, host) => { cfg.ReceiveEndpoint(host, RabbitMqConstants.GreetingEventSubscriberAQueue, e => { e.Consumer<GreetingEventConsumer>(); }); }); bus.Start(); 定义GreetingEventConsumer: 1 2 3 4 5 6 7 public class GreetingEventConsumer:IConsumer<Greeting.Message.GreetingEvent> { public async Task Consume(ConsumeContext<Greeting.Message.GreetingEvent> context) { await Console.Out.WriteLineAsync($"receive greeting event: id {context.Message.Id}"); } } 这一代码跟Masstransit.RabbitMQ.GreetingServer接受一个命令几乎一模一样,唯一的区别在于: 在Send/Receive模式中Client首先要获得对方(Server)的终结点(endpoint),直接向该终结点发送命令。Server方监听自己的终结点并消费命令。 而Publish/Subscribe模式中Client publish一个事件,SubscriberA在自己的终结点(endpointA)监听事件,SubscriberB在自己的终结点(endpointB)监听事件。 3、根据上面的分析再定义一个Masstransit.RabbitMQ.GreetingEvent.SubscriberB 4、将4个控制台应用程序跑起来看看 六、实现RPC模式 这一模式在Masstransit中被称作Request/Response模式,通过IRequestClient<IRequest, IResponse> 接口来实现相关操作。一个相关的例子在官方的github。 结束语:本篇文章分析了如何使用Masstransit来抽象业务,避免直接使用具体的消息队列,当然本文提到的众多服务总线机制,如“重试、熔断等”并没有在该文中出现,需要大家进一步去了解该项目。 通过对Masstransit的一些试用和NServiceBus的对比,Masstransit在实际项目中很容易上手并且免费,各种API定义的也非常清晰,但是官方的文档有点过于简单,实际使用中还需要去做深入的研究。作为.NET平台下为数不多的ESB开源产品,其关注程度还是不够,期待大家为开源项目做出贡献。 本文例子提供下载:http://git.oschina.net/richieyangs/RabbitMQ.Practice 来源:http://www.cnblogs.com/richieyang/ 分类: 软件设计 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7152014.html如需转载请自行联系原作者
本文大纲: 1. 小型电商网站的架构 2. 日志与监控系统的解决方案 3. 构建数据库的主从架构 4. 基于共享存储的图片服务器架构 5. 移动M站建设 6. 系统容量预估 7. 缓存系统 一、小型电商网站的架构 刚从传统软件行业进入到电商企业时,觉得电商网站没有什么技术含量,也没有什么门槛,都是一些现有的东西堆积木似的堆出来罢了。然而,真正进入到这个行业之后,才发现并非如此。有人说过,好的架构,是演化出来的,电商网站的架构也是如此。现在好的电商网站,看似很复杂,很牛逼,其实也是从很小的架构,也是从没什么技术含量开始的。所以,架构的演化过程,就是在技术团队不断追求极致的过程。 今天就来总结小型电商网站的架构演进。一套电商系统最初期的架构,往往会采用一个比较典型的LAMP架构,前端加上Apache/PHP,后端是MySQL。这个算是比较流行的。不过,目前还有一套.net的技术架构,可能大家很少提到。很不幸,我就是在一个.net平台为基础的电商公司。所以,今天也是要总结.net 平台的电商架构。 1技术架构 一般初期的电商网站,基本就几个业务子系统:网站前台、商家前台、系统管理后台、App、M站等。业务量也不是很大。所以,MVC + 缓存 + 数据库基本就搞定了。 单就开发效率而言,.net MVC 的技术架构不会比LAMP开发速度慢。所以,一些企业,为了快速推出自己的电商平台,也会采用.net 架构。 2基础架构 上图为基础架构层面。这是一个很简单的基础架构。 前端网站和M站,考虑到访问量和系统的可用性,基本会采用分布式部署。通过代理服务器进行请求分发。 其它的业务子系统,像商家前台和管理系统,基本上都是单机或是主从部署。 各个DB ,Redis 服务和文件和图片服务,搜索引擎Solr服务等,采用主从部署。 3详细架构 整个系统架构里面,还有一个比较重要的组成部分,那就是监控系统。例如:流量监控、硬件监控、系统性能监控等, 还有就是对某个页面进行监控,设置页面的其中一块进行监控等。它是提高整个平台可用性的一个重要手段。多平台、多个维度的监控,能够确保系统的可用性。一旦出现异常,特别在硬件或者性能方面出现异常,监控系统也能立刻发出警告,这样也好防范于未然。 总而言之,一个好的系统架构应该从扩展性、安全性、性能和可靠性来考虑。罗马不是一天建成的,架构适合就行,可以先行之而后优。通过渐进演化的过程,逐步让系统越来越完善。 二、日志与监控系统的解决方案 监控系统主要用于服务器集群的资源和性能监控,以及应用异常、性能监控、日志管理等多维度的性能监控分析。一个完善的监控系统和日志系统对于一个系统的重要性不必多说。总之,只有实时了解各系统的状态,才能保证各系统的稳定。 如上图所示,监控平台监控的范围很广,从服务器性能及资源,到应用系统的监控。每个公司都有特定的平台统一监控的需求及解决方案,但监控平台的任务和作用基本是一致的。 1日志 日志是监视程序运行的一种重要的方式,主要有两个目的:1.bug的及时发现和定位;2.显示程序运行状态。 正确详细的日志记录能够快速的定位问题。同样,通过查看日志,可以看出程序正在做什么,是不是按预期的设计在执行,所以记录下程序的运行状态是必要的。这里将日志分为两种:1.异常日志;2.运行日志。 我们主要是使用log4net,将各个系统的日志,持久化记录到数据库或者文件中,以方便后续的系统异常监控和性能分析。如何集成log4net,这里不多说。 日志记录的几个原则: 日志级别一定要区分清楚,哪些属于error、warning、info等。 记录错误的位置。如果是分层系统,一定要在某个层统一处理,例如我们的MVC架构,都是在各个Action中Catch异常并处理,而业务层和数据库层这些地方的异常,都是Catch到异常后,往上一层抛。 日志信息清晰准确有意义,日志尽量详细点,以方便处理。应该记录相关系统、模块、时间、操作人、堆栈信息等。方便后续处理。 2监控 监控系统是一个复杂的系统平台,目前有很多的开源产品和平台。不过我们平台小,监控任务和需求少,所以基本都是自己开发。主要有这五个方面:1.系统资源;2.服务器;3.服务;4.应用异常;5.应用性能。 具体的架构图如下: 1)系统资源监控 监控各种网络参数和各服务器相关资源(CPU、内存、磁盘读写、网络、访问请求等),保证服务器系统的安全运营,并提供异常通知机制以让系统管理员快速定位/解决存在的各种问题。目前比较流行的应该是Zabbix。 2)服务器监控 服务器的监控,主要是监控各个服务器、网络节点、网关等网络设备的请求响应是否正常。通过定时服务,定时去Ping各个网络节点设备,以确认各网络设备是否正常。如果哪个网络设备出现异常,则发出消息提醒。 3)服务监控 服务监控,指的是各个Web服务、图片服务、搜索引擎服务、缓存服务等平台系统的各项服务是否正常运行。可以通过定时服务,每隔一段时间,就去请求相关的服务,以确保平台的各项服务正常运行。 4)应用异常监控 目前我们平台所有系统的异常记录,都记录在数据库中。通过定时服务,统计分析一段时间之内的异常记录。如果发现有相关重要的模块的系统异常,比如支付、下单模块频繁发生异常,则立即通知相关人员处理,确保服务正常运行。 5)应用性能监控 在API接口和各应用的相关位置进行拦截和记录下程序性能(SQL性能,或是 程序执行效率)。相关重要模块提供性能预警,提前发现问题。 同时统计相关监控信息并显示给开发的人员,以方便后续的性能分析。 三、构建数据库的主从架构 发展到大型成熟的公司之后,主从架构可能就有点落伍了,取而代之的是更加复杂的数据库集群。但作为一个小型电商公司,数据库的主从架构应该是最基础的。任何大型的系统架构,都是不断演进的。主从架构便是数据库架构中最基础的架构。所以研究完主从架构,也就能看懂更加复杂的架构了。 首先为什么要读写分离? 对于一个小型网站,可能单台数据库服务器就能满足需求。但在一些大型的网站或者应用中,单台的数据库服务器可能难以支撑大的访问压力,升级服务器性能成本又太高,所以必须要横向扩展。还有就是,单库的话,读、写都是操作一个数据库。数据多了之后,对数据库的读、写性能就会有很大影响。同时对于数据安全性和系统的稳定性也是挑战。 数据库的读写分离的好处? 将读操作和写操作分离到不同的数据库上,避免主服务器出现性能瓶颈; 主服务器进行写操作时,不影响查询应用服务器的查询性能,降低阻塞,提高并发; 数据拥有多个容灾副本,提高数据安全性,同时当主服务器故障时,可立即切换到其他服务器,提高系统可用性。 读写分离的基本原理就是让主数据库处理事务性增、改、删操作(Insert、Update、Delete)操作,而从数据库处理Select查询操作。数据库复制被用来把事务性操作导致的变更同步到其它从数据库。 以SQL为例,主库负责写数据、读数据。读库仅负责读数据。每次有写库操作,同步更新到读库。写库就一个,读库可以有多个,采用日志同步的方式实现主库和多个读库的数据同步。 1SQL Server 读写分离的配置 SQL Server提供了三种技术,可以用于主从架构之间的数据同步的实现:日志传送、事务复制和SQL 2012 中新增的功能Always On 技术。各自优劣,具体的大家自己去百度吧,这里提供网上的朋友的配置方式,仅供参考。 日志传送:SQL Server 2008 R2 主从数据库同步 (链接:http://www.it165.net/database/html/201306/4088.html) 事务复制:SQL Server 复制:事务发布 (链接:http://www.cnblogs.com/gaizai/p/3305879.html) (图源:网络) 2C# 数据库读写操作 C#的请求数据库操作,单数据库和主从架构的数据库还是不一样的。主从架构的数据库,为了保证数据一致性,一般主库可读可写,从库只负责读,不负责写入。所以,实际C#在请求数据库时,要进行区别对待。 最简单的就是:配置两个数据库连接,然后在各个数据库调用的位置,区分读写请求相应的数据库服务器,如下图: 第二种解决方案就是判断SQL语句是写语句(Insert 、Update、Create、 Alter)还是读语句(Select)。 Demo下载:http://files.cnblogs.com/files/zhangweizhong/Weiz.DB.rar (PS:此Demo为本人总结,跟实际生产中的DLL 不太相同,但原理是一样的,大家总结封装吧) 同时,增加相关的数据库配置 四、基于共享存储的图片服务器架构 在当前这个互联网的时代,不管何种网站,对图片的需求量越来越大。尤其是电商网站,几乎都会面临到海量图片资源的存储、访问等相关技术问题。在对图片服务器的架构、扩展、升级的过程中,肯定也会碰到各种各样的问题与需求。当然这并不代表,你就必须得弄一个特别NB的图片服务架构,只要简单、高效、稳定就行。这部分我们来总结一个特别简单、高效的图片服务架构:通过共享存储的方式来实现图片服务架构。 然而,也有一些人问我,现在大型网站的图片服务器的架构已经完全不是这样了,别人家的图片系统比你这个牛逼多了,为啥不直接写那个呢? 事实是:第一,大型牛逼的系统我也不会;第二, 再牛逼的系统也是从小的架构演化过去的,没有一步到位的。这里介绍图片服务器架构虽然比较简单,但也是经过了单机时代的演化了,基本上可以满足中小型分布式网站的需求。这种架构的搭建和学习成本都极低,符合目前“短平快”的开发模式。 通过共享目录的方式实现共享存储 ,在共享目录文件服务器上配置独立域名,这样可以将图片服务器和应用服务器进行分离,来实现独立图片服务器。 优点: 1. 将图片服务和应用服务分离,缓解应用服务器的I/O负载。 2. 通过共享目录的方式来进行读写操作,可以避免多服务器之间同步相关的问题。 3. 相对来讲很灵活,也支持扩容/扩展。支持配置成独立图片服务器和域名访问,方便日后的扩展和优化。 4. 相对于更加复杂的分布式的NFS系统,这种方式是性价比高,符合目前互联网的“短平快”的开发模式。 缺点 : 1. 共享目录配置有些繁琐。 2. 会造成一定的(读写和安全)性能损失。 3. 如果图片服务器出现问题,那所有的应用都会受到影响。同时也对存储服务器的性能要求特别高。 4. 图片上传操作,还是得经过Web服务器,这对Web服务器还是有巨大的压力。 架构非常简单,基本架构如下图所示: 在存储服务器上建立一个共享目录(具体方式,我就不去重复了,自己百度吧,注意共享目录的文件安全)。 各个应用直接通过共享目录(\\192.168.1.200),将图片上传到存储服务器上。 建立一个Web站点(i1.abc.com)将该共享目录通过Web站点发布出去。这样其它的应用就能访问到相关图片。 所以,各应用将文件上传到共享目录 //保存原图 //完整的地址:\\192.168.1.200\lib\2016\03\04\10\IMG\4ugvvt6m9gdu.jpg relativePath = relativeDir + fileName + imageExtension; var absolutePath = ConfigHelper.SharePath + relativePath; fileData.SaveAs(absolutePath); 上传成功后,可直接通过web 的方式访问: http://i1.abc.com/lib/2016/03/04/10/IMG/4ugvvt6m9gdu.jpg 五、移动M站建设 最近在一直在搞M站,也就是移动Web站点。由于是第一次,也遇到了很多问题,所以把最近了解到的东西总结一番。聊一聊什么是移动M站,以及它有什么作用和优势。 有人会问,M站和APP有什么不同? APP直接在用户的移动设备上,曝光率相对较高。 而M站需打开浏览器,输入地址才能访问,所以曝光率相对较低。 M站的推广的渠道相比移动APP,渠道较多,方便追踪用户来源、流量入口等,方便以后的活动推广和数据分析。 M站用户无需安装,输入URL即可访问,而APP需要下载安装。 M站能够快速地通过数据分析,能快速得到用户的反馈,从而更容易根据统计数据分析和用户的需求来调整产品。 APP对用户更具粘性及用户体验也更好。 M站对于营销推广活动非常方便,转发分享方便快捷。 M站更新迭代产品速度和响应产品调整非常快,随时发布,而APP需要审核时间。 M站跨平台,无需开发安卓和iOS版,只需有浏览器即可。 所以, 我觉得,M站和客户端是相辅相成的。M站的及时性和快捷性,是APP无法比拟的。而APP的用户体验,则是M站无法做到的。目前来说两者是不可能被对方完全替代的,在互联网营销大行其道的今天,M站也越来越重要。营销活动大多以H5页面的形式展示和传播。通过M站的营销和推广,从而又促进APP的使用和推广。 目前,移动M站有倾向APP的趋势。M站会越来越像个APP,使得M站也越来越重要。而且,很多APP的展示效果,在原生代码无法实现的时候,嵌套移动H5页面也是一个很好的选择。 下面介绍几个移动M站建设的要点: 151Degree 51Degrees号称是目前最快、最准确的设备检测的解决方案。它是一个免费开源的.NET移动应用开发组件,可以用来检测移动设备和浏览器。甚至可以获取屏幕尺寸、输入法、加上制造商和型号信息等。从而可以选择性地被定向到为移动设备而设计的内容。由于拥有精确的移动设备的数据,所以几乎支持所有的智能手机,平板电脑等移动设备。 其实说白了,51Degree的作用就是识别客户端的设备。PC浏览器访问,就跳转到PC站,手机浏览器访问就跳转到M站。从而达到更好的用户体验。 如何将51Degree加入到现有网站? http://51degrees.codeplex.com/wikipage?title=Enhance%20existing%20web%20site 2架构 移动Web和传统的Web其实并没有本质的区别。说白了还是一个Web站点,使用的技术都是Html+CSS+JS。不同的是,只不过目前在Html5的大趋势下,将Html5加入到了移动M站,使得M站更像个轻APP。 3Bootstrap Bootstrap就不多说了,网上有很多Bootstrap的资料。它最大的优势应该就是非常流行,非常容易上手。如果缺少专业的设计或美工,那么Bootstrap是一个比较好的选择。他的用法极其简单,几乎没什么学习成本,绝对是快速开发的利器。 官网:http://getbootstrap.com/ Github:https://github.com/twbs/bootstrap/ 4几点建议 移动M站的URL要尽量和PC相同,这是可以避免同一URL在PC站可以显示,但是在手机上打开却是404; M站写单独的TDK。 六、系统容量预估 电商公司的朋友,这样的场景是否似曾相识: 运营和产品神秘兮兮的跑过来问:我们晚上要做搞个促销,服务器能抗得住么?如果扛不住,需要加多少台机器? 于是,技术一脸懵逼。 其实这些都是系统容量预估的问题,容量预估是架构师必备的技能之一。所谓,容量预估其实说白了就是系统在Down掉之前,所能承受的最大流量。这个是技术人员对于系统性能了解的重要指标。常见的容量评估包括流量、并发量、带宽、CPU、内存 、磁盘等一系列内容。这部分来聊一聊容量预估的问题。 1几个重要参数 QPS:每秒钟处理的请求数。 并发量: 系统同时处理的请求数。 响应时间: 一般取平均响应时间。 很多人经常会把并发数和QPS给混淆了。其实只要理解了上面三个要素的意义之后,就能推算出它们之间的关系:QPS = 并发量 / 平均响应时间。 2容量评估的步骤与方法 1)预估总访问量 如何知道总访问量?对于一个运营活动的访问量评估,或者一个系统上线后PV的评估,有什么好方法? 最简单的办法就是:询问业务方,询问运营同学,询问产品同学,看产品和运营对此次活动的流量预估。 不过,业务方对于流量的预估,应该就PV和用户访问数这两个指标。技术人员需要根据这两个数据,计算其他相关指标,比如QPS等。 2)预估平均QPS 总请求数=总PV*页面衍生连接数 平均QPS = 总请求数/总时间 比如:活动落地页1小时内的总访问量是30w PV,该落地页的衍生连接数为30,那么落地页的平均QPS=(30w*30)/(60*60)=2500。 3)预估峰值QPS 系统容量规划时,不能只考虑平均QPS,还要考虑高峰的QPS,那么如何评估峰值QPS呢? 这个要根据实际的业务评估,通过以往的一些营销活动的PV等数据进行预估。一般情况下,峰值QPS大概是均值QPS的3-5倍,如果日均QPS为1000,于是评估出峰值QPS为5000。 不过,有一些业务会比较难评估业务访问量,例如“秒杀业务”,这类业务的容量评估暂时不在此讨论。 4)预估系统、单机极限QPS 如何预估一个业务,一个服务器单机的极限QPS呢? 这个性能指标是服务器最基本的指标之一,所以除了压力测试没有其他的办法。通过压力测试,算出服务器的单机极限QPS 。 在一个业务上线前,一般都需要进行压力测试(很多创业型公司,业务迭代很快的系统可能没有这一步,那就悲剧了),以APP推送某营销活动为例(预计日均QPS为1000,峰值QPS为5000),业务场景可能是这样的: 通过APP推送一个活动消息; 运营活动H5落地页是一个Web站点; H5落地页由缓存Cache和数据库DB中的数据拼装而成。 通过压力测试发现,Web服务器单机只能抗住1200的QPS,Cache和数据库DB能抗住并发压力(一般来说,1%的流量到数据库,数据库120 QPS还是能轻松抗住的,Cache的话QPS能抗住,需要评估Cache的带宽,这里假设Cache不是瓶颈),这样,我们就得到了Web单机极限的QPS是1200。一般来说,生产系统不会跑满到极限的,这样容易影响服务器的寿命和性能,单机线上允许跑到QPS1200*0.8=960。 扩展说一句,通过压力测试,已经知道Web层是瓶颈,则可针对Web相关的方面做一些调整优化,以提高Web服务器的单机QPS 。 还有压力测试工作中,一般是以具体业务的角度进行压力测试,关心的是某个具体业务的并发量和QPS。 5)回答最开始那两个问题 需要的机器=峰值QPS/单机极限QPS 好了,上述已经得到了峰值QPS是5000,单机极限QPS是1000,线上部署了3台服务器: 服务器能抗住么? -> 峰值5000,单机1000,线上3台,扛不住 如果扛不住,需要加多少台机器? -> 需要额外2台,提前预留1台更好,给3台保险 3总结 需要注意的是,以上都是计算单个服务器或是单个集群的容量。实际生产环境是由Web、消息队列、缓存、数据库等等一系列组成的复杂集群。在分布式系统中,任何节点出现瓶颈,都有可能导致雪崩效应,最后导致整个集群垮掉 (“雪崩效应”指的是系统中一个小问题会逐渐扩大,最后造成整个集群宕机)。 所以,要了解规划整个平台的容量,就必须计算出每一个节点的容量。找出任何可能出现的瓶颈所在。 七、缓存系统 对于一个电商系统,缓存是重要组成部分,而提升系统性能的主要方式之一也是缓存。它可以挡掉大部分的数据库访问的冲击,如果没有它,系统很可能会因为数据库不可用导致整个系统崩溃。 但缓存带来了另外一些棘手的问题: 数据的一致性和实时性。例如,数据库中的数据状态已经改变,但在页面上看到的仍然是缓存的旧值,直到缓冲时间失效之后,才能重新更新缓存。这个问题怎么解决? 还有就是缓存数据如果没有失效的话,是会一直保持在内存中的,对服务器的内存也是负担,那么,什么数据可以放缓存,什么数据不可以,这是系统设计之初必须考虑的问题。 什么数据可以放缓存? 不需要实时更新但是又极其消耗数据库的数据。比如网站首页的商品销售的排行榜,热搜商品等等,这些数据基本上都是一天统计一次,用户不会关注其是否是实时的。 需要实时更新,但是数据更新的频率不高的数据。 每次获取这些数据都经过复杂的处理逻辑,比如生成报表。 什么数据不应该使用缓存? 实际上,在电商系统中,大部分数据都是可以缓存的,不能使用缓存的数据很少。这类数据包括涉及到钱、密钥、业务关键性核心数据等。总之,如果你发现,系统里面的大部分数据都不能使用缓存,这说明架构本身出了问题。 如何解决一致性和实时性的问题? 保证一致性和实时性的办法就是:一旦数据库更新了,就必须把原来的缓存更新。 说一说我们的缓存方案:我们目前的缓存系统:Redis(主从)+ RabbitMQ + 缓存清理服务组成,具体如下图: 缓存清理作业订阅RabbitMQ消息队列,一有数据更新进入队列,就将数据重新更新到Redis缓存服务器。 当然,有些朋友的方案,是数据库更新完成之后,立马去更新相关缓存数据。这样就不需要MQ和缓存清理作业。不过,这同时也增加了系统的耦合性。具体得看自己的业务场景和平台大小。 转: http://mp.weixin.qq.com/s/4E7kzWQ5OOUOv6UOSXs0PA 分类: 软件设计 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/justinw/,如需转载请自行联系原作者
最近安装的IE8浏览器,今天打开VisualStudio想调试一个程序,发现程序运行时,打开IE页面后,Debug启动,很快就关闭了。 在网上遍寻解决方案,终于找到了。 解决办法如下: 在程序运行中输入regedit打开注册表, 选择HKEY_LOCAL_MACHINE->SOFTWARE->Microsoft->Internet Explorer->Main 在Main点击右键新建DWORD值,名称定义为TabProcGrowth 双击TabProcGrowth,在数据数据里填0,确认。 就OK了! 希望能帮到遍到此问题的朋友们! 以下是参考网站: http://social.microsoft.com/Forums/en-US/vsdebug/thread/e2c795cd-b7a0-4fad-b7c9-b1ca40d7302e 原文地址:http://www.cnblogs.com/fantasyice/archive/2009/07/17/1525819.html 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/10/09/1579665.html,如需转载请自行联系原作者
文/陈皓 yuanwendizhi:http://news.cnblogs.com/n/104153/ 酷壳里有很多我觉得很不错的文章,但是访问量最大的却是那篇《6个变态的Hello World》,和它能在本站右边栏“全站热门”中出现的还有“如何加密源代码”,以及编程真难啊等这样的文章。可见本站的读者们的偏好,我也相信你们都是“身怀绝技”的程序员。所以,今天给大家推荐这篇文章,相信一定能触动大家的兴奋点。 这篇文章的原文在这里(http://mindprod.com/jgloss/unmain.html),我看完后我想说—— 什么叫“创造力”,创造力就是——就算是要干一件烂事,都能干得那么漂亮,那么有创意的能力。 什么叫“抓狂”,抓狂就是——以一种沉着老练的不屈不挠的一本正经的精神,一点一点把你推向崩溃的边缘。 我把文章节选了一些,也并没有完全翻译,简译一下,也加入了一些自己的调侃。对于有下面这些编程习惯的朋友,请大家对号入座。另外,维护程序的朋友们,你们死定了!! If builders built buildings the way programmers write programs, then the first woodpecker that came along would destroy civilization. (如果建筑师盖房子就像程序员写程序一样,那么,第一只到来的啄木鸟就能毁掉我们的文明) ~ Gerald Weinberg (born: 1933-10-27 age: 77) Weinberg’s Second Law 程序命名 容易输入的名字。比如:Fred,asdf 单字母的变量名。比如:a,b,c,x,y,z(陈皓注:如果不够用,可以考虑a1,a2,a3,a4,….) 有创意地拼写错误。比如:SetPintleOpening, SetPintalClosing。这样可以让人很难搜索代码。 抽象。比如:ProcessData, DoIt, GetData…抽象到就跟什么都没说一样。 缩写。比如:WTF,RTFSC ……(陈皓注:使用拼音缩写也同样给力,比如: BT,TMD,TJJTDS) 随机大写字母。比如:gEtnuMbER.. 重用命名。在内嵌的语句块中使用相同的变量名有奇效。 使用重音字母。比如:int ínt(注:第二个 ínt不是int) 使用下划线。比如:_, __, ___。 使用不同的语言。比如混用英语,德语,或是中文拼音。 使用字符命名。比如:slash, asterix, comma… 使用无关的单词。比如:god, superman, iloveu…. 混淆l和1。字母l和数字1有时候是看不出来的。 伪装欺诈 把注释和代码交织在一起。 for(j=0; j<array_len; j+ =8)<br /> {<br /> total += array[j+0 ];<br /> total += array[j+1 ];<br /> total += array[j+2 ]; /* Main body of<br /> total += array[j+3]; * loop is unrolled<br /> total += array[j+4]; * for greater speed.<br /> total += array[j+5]; */<br /> total += array[j+6 ];<br /> total += array[j+7 ];<br /> } 隐藏宏定义。如:#define a=b a=0-b,当人们看到a=b时,谁也想不到那是一个宏。 换行。如下所示,下面的示例使用搜索xy_z变得困难。 #define local_var xy\<br /> _z // local_var OK 代码和显示不一致。比如,你的界面显示叫postal code,但是代码里确叫zipcode. 隐藏全局变量。把使用全局变量以函数参数的方式传递给函数,这样可以让人觉得那个变量不是全局变量。 使用同意词。如: #define xxx global_var // in file std.h&nbsp;<br /> #define xy_zxxx// in file ..\other\substd.h&nbsp;<br /> #define local_var xy_z// in file ..\codestd\inst.h 使用相似的变量名。如:单词相似,swimmer 和 swimner,字母相似:ilI1 或 oO08。parselnt 和 parseInt, D0Calc 和 DOCalc。还有这一组:xy_Z,xy__z, _xy_z, _xyz, XY_Z,xY_z, Xy_z。 重载函数。使用相同的函数名,但是其功能和具体实现完全没有关系。 操作符重载。重载操作符可以让你的代码变得诡异,感谢CCTV,感谢C++。这个东西是可以把混乱代码提高到一种艺术的形式。比如:重载一个类的!操作符,但实际功能并不是取反,让其返回一个整数。于是,如果你使用!!操作符,那么,有意思的事就发生了——先是调用类的重载!操作符,然后把其返回的整数给!成了布尔变量,如果是!!!呢?呵呵。 #define。看过本站那些混乱代码的文章,你都会知道宏定义和预编译对于写出不可读的代码的重大意义。不过,一个具有想像力的东西是——在头文件中使用预编译来查看这个头文件被include了几次,而被include不同的次数时,其中的函数定义完全不一 #ifndef DONE </p> <p>#ifdef TWICE </p> <p>// put stuff here to declare 3rd time around<br /> void g(char* str);<br /> #define DONE </p> <p>#else // TWICE<br /> #ifdef ONCE </p> <p>// put stuff here to declare 2nd time around<br /> void g(void* str);<br /> #define TWICE </p> <p>#else // ONCE </p> <p>// put stuff here to declare 1st time around<br /> void g(std::string str);<br /> #define ONCE </p> <p>#endif // ONCE<br /> #endif // TWICE<br /> #endif // DONE 文档和注释 在注释中撒谎。你不用真的去撒谎,只需在改代码的时候不要更新注释就可以了。 注释明显的东西。比如:/* add 1 to i */。(参看本站的“五种应该避免的注释”) 只注释是什么,而不是为什么。 不要注释秘密。如果你开发一个航班系统,请你一定要保证每有一个新的航班被加入,就得要修改25个以上的位置的程序。千万别把这个事写在文档中。 注重细节。当你设计一个很复杂的算法的时候,你一定要把所有的详细细设计都写下来,没有100页不能罢休,段落要有5级以上,段落编号要有500个以上,例如:1.2.4.6.3.13 – Display all impacts for activitywhere selected mitigations can apply(short pseudocode omitted). 这样,当你写代码的时候,你就可以让你的代码和文档一致,如:Act1_2_4_6_3_13() 千万不要注释度衡单位。比如时间用的是秒还是毫秒,尺寸用的是像素还是英寸,大小是MB还是KB。等等。另外,在你的代码里,你可以混用不同的度衡单位,但也不要注释。 Gotchas。陷阱,千万不要注释代码中的陷阱。 在注释和文档中发泄不满。(参看本站的“五种应该避免的注释”) 程序设计 Java Casts。Java的类型转型是天赐之物。每一次当你从Collection里取到一个object的时候,你都需要把其转回原来的类型。因些,这些转型操作会出现在N多的地方。如果你改变了类型,那么你不一定能改变所有的地方。而编译器可能能检查到,也可能检查不到。 利用Java的冗余。比如:Bubblegum b = new Bubblegom(); 和 swimmer = swimner + 1; 注意变量间的细微差别。 从不验证。从不验证输入的数据,从不验证函数的返回值。这样做可以向大家展示你是多么的信任公司的设备和其它程序员。 不要封装。调用者需要知道被调用的所有的细节。 克隆和拷贝。为了效率,你要学会使用copy+ paste。你几乎都不用理解别人的代码,你就可以高效地编程了。(陈皓注:Copy+ Paste出来的代码bug多得不能再多) 巨大的listener。写一个listener,然后让你的所有的button类都使用这个listener,这样你可以在这个listener中整出一大堆if…else…语句,相当的刺激。 使用三维数组。如果你觉得三维还不足够,你可以试试四维。 混用。同时使用类的get/set方法和直接访问那个public变量。这样做的好处是可以极大的挫败维护人员。 包装,包装,包装。把你所有的API都包装上6到8遍,包装深度多达4层以上。然后包装出相似的功能。 没有秘密。把所有的成员都声明成public的。这样,你以后就很难限制其被人使用,而且这样可以和别的代码造成更多的耦合度,可以让你的代码存活得更久。 排列和阻碍。把drawRectangle(height,width)改成 drawRectangle(width, height),等release了几个版本后,再把其改回去。这样维护程序的程序员们将不能很快地明白哪一个是对的。 把变量改在名字上。例如,把setAlignment(int alignment)改成,setLeftAlignment, setRightAlignment, setCenterAlignment。 Packratting。保留你所有的没有使用的和陈旧的变量,方法和代码。 That’s Final。Final你所有的子结点的类,这样,当你做完这个项目后,没有人可以通过继承来扩展你的类。java.lang.String不也是这样吗? 避免使用接口。在java中,BS接口,在C++中BS使用虚函数。 避免使用layout。这样就使得我们只能使用绝对坐标。如果你的老大强制你使用layout,你可以考虑使用GridBagLayout,然后把grid坐标hard code. 环境变量。如果你的代码需要使用环境变量。(getenv()– C++ / System.getProperty()– Java ),那么,你应该把你的类的成员的初始化使用环境变量,而不是构造函数。 使用Magic number。参看《Linux一个插曲》。 使用全局变量。1)把全局变量的初始化放在不同的函数中,就算这个函数和这个变量没有任何关系,这样能够让我们的维护人员就像做侦探工作一样。2)使用全局变量可以让你的函数的参数变得少一些。 配置文件。配置文件主要用于一些参数的初始化。在编程中,我们可以让配置文件中的参数名和实际程序中的名字不一样。 膨胀你的类。让你的类尽可能地拥有各种臃肿和晦涩的方法。比如,你的类只实现一种可能性,但是你要提供所有可能性的方法。不要定义其它的类,把所有的功能都放在一个类中。 使用子类。面向对象是写出无法维护代码的天赐之物。如果你有一个类有十个成为(变量和方法)你可以考虑写10个层次的继承,然后把这十个属性分别放在这十个层次中。如果可能的话,把这十个类分别放在十个不同的文件中。 混乱你的代码 使用XML。XML的强大是无人能及的。使用XML你可以把本来只要10行的代码变成100行。而且,还要逼着别人也有XML。(参看,信XML得永生,信XML得自信) 混乱C代码。在《如何加密源代码》中已经说过一些方法了,这里再补充一些。 使用不同的进制。比如:10 和010不是一样的。再比如:array = new int[]{ 111, 120, 013, 121,}; 尽量使用void*。然后把其转成各种类型 使用隐式的转型。C++的构造函数可以让你神不知鬼不觉得完成转型。 分解条件表达式。如:把 a==100分解成,a>99 && a<101 学会利用分号。如:if ( a );else;{ int d; d = c;} 间接转型。如:把double转string,写成new Double(d).toString()而不是 Double.toString(d) 大量使用嵌套。一个NB的程序员可以在一行代码上使用超过10层的小括号(),或是在一个函数里使用超过20层的语句嵌套{},把嵌套的if else 转成 [? :] 也是一件很NB的事。 使用C的变种数组。myArray[i] 可以变成*(myArray+ i)也可以变成*(i + myArray)其等价于 i[myArray]。再看一个函数调用的示例,函数声明:int myfunc(int q, int p){ return p%q; }函数调用myfunc(6291, 8)[Array]; 长代码行。一行的代码越长越好。这样别人阅读时就需要来来回回的 不要较早的return。不要使用goto,不要使用break,这样,你就需要至少5层以上的if-else来处理错误。 不要使用{}。不要在if else使用{},尤其是在你重量地使用if-else嵌套时,你甚至可以在其中乱缩进代码,这样一来,就算是最有经验的程序员也会踩上陷阱。 使用宏定义。宏定义绝对是混乱C/C++代码的最佳利器。参看 老手是这样教新手编程的。 琐碎的封装。比较封装一个bool类,类里面什么都做,就是一个bool. 循环。千万不可用for(int i=0; i<n; i++)使用while代替for,交换n和i,把<改成<=,使用 i–调整步伐。 测试 从不测试。千万不要测试任何的出错处理,从来也不检测系统调用的返回值。 永远不做性能测试。如果不够快就告诉用户换一个更快的机器。如果你一做测试,那么就可能会要改你的算法,甚至重设计,重新架构。 不要写测试案例。不要做什么代码覆盖率测试,自动化测试。 测试是懦夫行为。一个勇敢的程序员是根本不需要这一步的。太多的程序太害怕他们的老板,害怕失去工作,害怕用户抱怨,甚至被起诉。这种担心害怕直接影响了生产力。如果你对你的代码有强大的信心,那还要什么测试呢?真正的程序员是不需要测试自己的代码的。 其它 你的老板什么都知道。无论你的老板有多SB,你都要严格地遵照他的旨意办事,这样一样,你会学到更多的知识如何写出无法维护的代码来的。 颠覆Help Desk。你要确保你那满是bug的程序永远不要被维护团队知道。当用户打电话和写邮件给你的时候,你就不要理会,就算要理会,让用户重做系统或是告诉用户其帐号有问题,是标准的回答。 闭嘴。对于一些像y2k这样的大bug,你要学会守口如瓶,不要告诉任何人,包括你的亲人好友以及公司的同事和管理层, 忽悠。你会学会忽悠,就算你的代码写得很烂,你也要为其挂上GoF设计模式的标签,就算你的项目做得再烂,你也要为其挂上敏捷的标签,只有学会像中国Thoughtworks的咨询师那样去忽悠,你才能学会更炫更酷的方法,让整个团队和公司,甚至整个业界都开始躁动,这样才能真正为难维护的代码铺平道路。 这个文档中还有很多很多,实在是太TMD强大了,大家自己去看看吧。有精力有能力的朋友不妨把其翻译成中文。 总之,我们的口号是—— Write Everywhere, Read Nowhere 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2011/06/03/2072022.html,如需转载请自行联系原作者
最近微博上出了一则这样的消息,腾讯北京的一个保安入职了腾讯研究院.哈哈.你们看到什么感觉? 你的广大员工之中,不乏才能出众的人。有的技术水平高,有的善于管理,有的擅于外交,各有所能。适时适度地选拔人才,提升一些有能力的人,不仅有利于本部门、本单位的发展,还可以利用这些被提升的员工,借以了解其他员工的思想状况,并据此有的放矢地做好员工的工作,不要动不动就到外界去高薪聘请,要改变那种“外来的和尚能念经”的用人观念.你所提升、选拔的员工,多少会对你有些感激,至少对你有信任感。当你的领导工作遇到困难的时候,他们会首先伸出手帮助你渡过难关。当你的工作万事俱备、只欠东风的时候,他们也往往会勇往直前,助你一臂之力,起到率先示范的作用。被提升的员工往往比你更容易接近其他员工,而且他们之间的关系通常也比较密切。 你是否能做到以下这些呢? 1、一个曾受到众人诽谤,大家公认不可救药的人,经过你的仔细考察,发现事实并非如此,这人很有才华工作技能也很高。因而,你大胆决定将这位员工提升上来。 2、一个曾经当众辱骂过你的员工,仍然因为他专业能力强,而被你不计前嫌地提拔到你的身边。 3、一个相貌、身材矮小的员工,你并不是以貌而取之,而是考虑到他的真才实学,把他从众人之中选拔上来。 4、一个过去是你的同事、老朋友,现在是你的员工,在你选拔、提升员工时,他与别人条件相同,但是,你并不因为与他是老朋友,而失去公平地优先提拔他。 5、对一个曾经犯过错误的员工,你能辩证地看待问题,发现这位员工的可贵之处和闪光点,经过一段时间对他的培养、考察,把他提升到一个新职位上。 6、一个知识、能力都比你强的员工,你不会因为嫉妒而不去提拔他,而是敢于把他提拔到重要的位置上来。 做到以上这些,才能使你的领导工作顺利开展,你的领导威信才能逐步建立。虽然你提拔的人才可能一时还不能做到令大家满意,但你不必过于着急,是金子,终归有一天会闪光的,这只是迟早的事情。关键是你提拔的员工是不是真正的金子。正确有效地提拔员工,能很好地证明你这位作为选拔者的领导所具有的用人素质。如果员工能从你用人的态度上感到你办事的公平合理和严格,那么,你就会受到员工的信任,你的领导地位才能更稳固。 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2012/03/03/2378022.html,如需转载请自行联系原作者
锁的概述 一. 为什么要引入锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 丢失更新 A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 脏读 A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致 不可重复读 A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致 并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一致 二 锁的分类 锁的类别有两种分法: 1. 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁 MS-SQL Server 使用以下资源锁模式。 锁模式 描述 共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。 更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。 排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。 意向锁 用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。 架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。 大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。 共享锁 共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。 更新锁 更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。 若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。 排它锁 排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。 意向锁 意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。 意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。 锁模式 描述 意向共享 (IS) 通过在各资源上放置 S 锁,表明事务的意向是读取层次结构中的部分(而不是全部)底层资源。 意向排它 (IX) 通过在各资源上放置 X 锁,表明事务的意向是修改层次结构中的部分(而不是全部)底层资源。IX 是 IS 的超集。 与意向排它共享 (SIX) 通过在各资源上放置 IX 锁,表明事务的意向是读取层次结构中的全部底层资源并修改部分(而不是全部)底层资源。允许顶层资源上的并发 IS 锁。例如,表的 SIX 锁在表上放置一个 SIX 锁(允许并发 IS 锁),在当前所修改页上放置 IX 锁(在已修改行上放置 X 锁)。虽然每个资源在一段时间内只能有一个 SIX 锁,以防止其它事务对资源进行更新,但是其它事务可以通过获取表级的 IS 锁来读取层次结构中的底层资源。 独占锁:只允许进行锁定操作的程序使用,其他任何对他的操作均不会被接受。执行数据更新命令时,SQL Server会自动使用独占锁。当对象上有其他锁存在时,无法对其加独占锁。 共享锁:共享锁锁定的资源可以被其他用户读取,但其他用户无法修改它,在执行Select时,SQL Server会对对象加共享锁。 更新锁:当SQL Server准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server确定要进行更新数据操作时,他会自动将更新锁换为独占锁,当对象上有其他锁存在时,无法对其加更新锁。 2. 从程序员的角度看:分为乐观锁和悲观锁。 乐观锁:完全依靠数据库来管理锁的工作。 悲观锁:程序员自己管理数据或对象上的锁处理。 MS-SQLSERVER 使用锁在多个同时在数据库内执行修改的用户间实现悲观并发控制 三 锁的粒度 锁粒度是被封锁目标的大小,封锁粒度小则并发性高,但开销大,封锁粒度大则并发性低但开销小 SQL Server支持的锁粒度可以分为为行、页、键、键范围、索引、表或数据库获取锁 资源 描述 RID 行标识符。用于单独锁定表中的一行。 键 索引中的行锁。用于保护可串行事务中的键范围。 页 8 千字节 (KB) 的数据页或索引页。 扩展盘区 相邻的八个数据页或索引页构成的一组。 表 包括所有数据和索引在内的整个表。 DB 数据库。 四 锁定时间的长短 锁保持的时间长度为保护所请求级别上的资源所需的时间长度。 用于保护读取操作的共享锁的保持时间取决于事务隔离级别。采用 READ COMMITTED 的默认事务隔离级别时,只在读取页的期间内控制共享锁。在扫描中,直到在扫描内的下一页上获取锁时才释放锁。如果指定 HOLDLOCK 提示或者将事务隔离级别设置为 REPEATABLE READ 或 SERIALIZABLE,则直到事务结束才释放锁。 根据为游标设置的并发选项,游标可以获取共享模式的滚动锁以保护提取。当需要滚动锁时,直到下一次提取或关闭游标(以先发生者为准)时才释放滚动锁。但是,如果指定 HOLDLOCK,则直到事务结束才释放滚动锁。 用于保护更新的排它锁将直到事务结束才释放。 如果一个连接试图获取一个锁,而该锁与另一个连接所控制的锁冲突,则试图获取锁的连接将一直阻塞到: 将冲突锁释放而且连接获取了所请求的锁。 连接的超时间隔已到期。默认情况下没有超时间隔,但是一些应用程序设置超时间隔以防止无限期等待 五 SQL Server 中锁的自定义 1 处理死锁和设置死锁优先级 死锁就是多个用户申请不同封锁,由于申请者均拥有一部分封锁权而又等待其他用户拥有的部分封锁而引起的无休止的等待 可以使用SET DEADLOCK_PRIORITY控制在发生死锁情况时会话的反应方式。如果两个进程都锁定数据,并且直到其它进程释放自己的锁时,每个进程才能释放自己的锁,即发生死锁情况。 2 处理超时和设置锁超时持续时间。 @@LOCK_TIMEOUT 返回当前会话的当前锁超时设置,单位为毫秒 SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待的时间大于 LOCK_TIMEOUT 设置时,系统将自动取消阻塞的语句,并给应用程序返回"已超过了锁请求超时时段"的 1222 号错误信息 示例 下例将锁超时期限设置为 1,800 毫秒。 SET LOCK_TIMEOUT 1800 3) 设置事务隔离级别。 4 ) 对 SELECT、INSERT、UPDATE 和 DELETE 语句使用表级锁定提示。 5) 配置索引的锁定粒度 可以使用 sp_indexoption 系统存储过程来设置用于索引的锁定粒度 六 查看锁的信息 1 执行 EXEC SP_LOCK 报告有关锁的信息 2 查询分析器中按Ctrl+2可以看到锁的信息 七 使用注意事项 如何避免死锁 1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务; 2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂; 3 优化程序,检查并避免死锁现象出现; 4 .对所有的脚本和SP都要仔细测试,在正是版本之前。 5 所有的SP都要有错误处理(通过@error) 6 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁 解决问题 如何对行 表 数据库加锁 八 几个有关锁的问题 1 如何锁一个表的某一行 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SELECT * FROM table ROWLOCK WHERE id = 1 2 锁定数据库的一个表 SELECT * FROM table WITH (HOLDLOCK) 加锁语句: sybase: update 表 set col1=col1 where 1=0 ; MSSQL: select col1 from 表 (tablockx) where 1=0 ; oracle: LOCK TABLE 表 IN EXCLUSIVE MODE ; 加锁后其它人不可操作,直到加锁用户解锁,用commit或rollback解锁 几个例子帮助大家加深印象 设table1(A,B,C) A B C a1 b1 c1 a2 b2 c2 a3 b3 c3 1)排它锁 新建两个连接 在第一个连接中执行以下语句 begin tran update table1 set A='aa' where B='b2' waitfor delay '00:00:30' --等待30秒 commit tran 在第二个连接中执行以下语句 begin tran select * from table1 where B='b2' commit tran 若同时执行上述两个语句,则select查询必须等待update执行完毕才能执行即要等待30秒 2)共享锁 在第一个连接中执行以下语句 begin tran select * from table1 holdlock -holdlock人为加锁 where B='b2' waitfor delay '00:00:30' --等待30秒 commit tran 在第二个连接中执行以下语句 begin tran select A,C from table1 where B='b2' update table1 set A='aa' where B='b2' commit tran 若同时执行上述两个语句,则第二个连接中的select查询可以执行 而update必须等待第一个事务释放共享锁转为排它锁后才能执行 即要等待30秒 3)死锁 增设table2(D,E) D E d1 e1 d2 e2 在第一个连接中执行以下语句 begin tran update table1 set A='aa' where B='b2' waitfor delay '00:00:30' update table2 set D='d5' where E='e1' commit tran 在第二个连接中执行以下语句 begin tran update table2 set D='d5' where E='e1' waitfor delay '00:00:10' update table1 set A='aa' where B='b2' commit tran 同时执行,系统会检测出死锁,并中止进程 补充一点: Sql Server2000支持的表级锁定提示 HOLDLOCK 持有共享锁,直到整个事务完成,应该在被锁对象不需要时立即释放,等于SERIALIZABLE事务隔离级别 NOLOCK 语句执行时不发出共享锁,允许脏读 ,等于 READ UNCOMMITTED事务隔离级别 PAGLOCK 在使用一个表锁的地方用多个页锁 READPAST 让sql server跳过任何锁定行,执行事务,适用于READ UNCOMMITTED事务隔离级别只跳过RID锁,不跳过页,区域和表锁 ROWLOCK 强制使用行锁 TABLOCKX 强制使用独占表级锁,这个锁在事务期间阻止任何其他事务使用这个表 UPLOCK 强制在读表时使用更新而不用共享锁 应用程序锁: 应用程序锁就是客户端代码生成的锁,而不是sql server本身生成的锁 处理应用程序锁的两个过程 sp_getapplock 锁定应用程序资源 sp_releaseapplock 为应用程序资源解锁 注意: 锁定数据库的一个表的区别 SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除 SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2012/08/24/2654412.html,如需转载请自行联系原作者
摘自阿里云服务器官网,此处 一键安装包下载: 点此下载 安装须知 1、此安装包可在阿里云所有linux系统上部署安装,此安装包包含的软件及版本为: nginx:1.0.15、1.2.5、1.4.4 apache:2.2.22、2.4.2 mysql:5.1.73、5.5.35、5.6.15 php:5.3.18、5.4.23、5.5.7 php扩展:memcache、Zend Engine/ OPcache ftp:(yum/apt-get安装) phpwind:8.7 GBK phpmyadmin:4.1.8 2、请使用最新的一键安装包脚本安装,以前老版本都存在bug,会导致安装异常以及安装的环境混乱。 3、执行一键安装包(./install.sh),会自动清理之前一键安装包安装过的环境。 如果您已经安装过一键安装包,再次执行安装,如若有重要数据,请自行备份/alidata目录。 4、一键安装包会将软件安装在/alidata目录下。 如果您的系统不存在数据盘,则一键安装包会将软件安装到系统/alidata目录下。 如果您的系统存在数据盘,且还没有格式化挂载数据盘。则一键安装包会自动帮您格式化第一块数据盘,并挂载在/alidata目录下。 如果您的系统存在数据盘,且已格式化并挂载了第一块数据盘。一键安装包会将第一块数据盘再次挂载到/alidata目录下,并不影响您之前的挂载目录的使用。比如下图所示,安装前,已经挂载了mnt目录。安装后,一键安装包再次挂载/alidata目录。 如果您的系统存在数据盘,且已格式化数据盘,但没有挂载数据盘。一键安装包会将第一块数据盘挂载到/alidata目录下。 5、怎么样卸载一键安装包? 可以执行以下命令完成卸载: chmod 777 -R sh-1.3.0/ cd sh-1.3.0/ ./uninstall.sh 好了,下面来进行我们详细的安装步骤,come on ~~! 首先准备好连接linux服务器的工具,推荐用xshell和xftp。 xshell 是一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。 下载地址:http://www.newhua.com/soft/36383.htmXftp 是一个基于 MS windows 平台的功能强大的SFTP、FTP 文件传输软件。 下载地址:http://www.newhua.com/soft/143.htm 根据上面提供的下载地址,首先安装xshell(基本上是点击Next直至安装 完成): 点击Next-->此时选择“free for Home/School”(免费版)。 再点击Next-->Next-->Next-->install即可安装(还可以选择xshell安装后的语言环境,熟悉英文的朋友,可以选择English选项)。 点击finish完成xshell的安装。 然后我们可以看到以下界面。 接下来安装xftp(用于上传文件到linux服务器的小工具),安装xftp也是非常的简单,基本上也点击Next直至安装完成。 点击Next -->此时选择“free for Home/School”,(免费版)。 录点击Next -->Next -->Next-->install (还可以选择xshell安装后的语言环境,熟悉英文的朋友,可以选择English选项)。 点击finish完成安装。 然后我们可以看到以下界。 然后我们打开xshell, 设置云服务器登录信息。 设置服务器帐号密码。 设置字符集编码(如果不设置字符集编码,则中文字符将会在xshell中显示为乱码)。 设置好了就点击ok保存。 一切准备就绪,让我们连接看看云linux服务器的庐山真面目吧! 黑不隆冬的,啥都没有,这就是linux的shell啦。shell(壳)到底是什么东东呢? * Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行。 linux作为服务器专用操作系统,主要就是默默的待在机房提供各项网络服务的,为了节省系统资源,像windows那样华丽丽的用户桌面就默认不加载了。当然现在越来越多的用户使用linux作为日常家庭办公用操作系统,所以就有了像gnome、kde这样超华丽的桌面。不过作为服务器使用还是推荐用命令行界面吧,毕竟省一点资源,网站打开可能会更快一点嘛! 打下面的命令去主目录(/home)看下 输入命令:cd 用xftp上传环境安装文件。 回到shell界面,用ll命令看下情况 输入命令:ll 是不是多了一个sh-1.3.0目录,1.3.0是我们的一键安装包的版本号。 请下载最新的版本安装,版本更新介绍,我们可以在change.log中查看。也希望大家多多反馈对于一键安装包的意见。(另外,熟悉linux的朋友,直接可以用wget命令下载我们的安装包,也不用安装Xftp,这样更加便捷。) 接下来按照说明,分步骤敲入安装命令(以下为redhat系统下安装示例)。 输入命令:chmod –R 777 sh-1.3.0 cd sh-1.3.0 ./install.sh 出现了一个选择提示,进入web服务器的选择界面。 这里您可以根据需要选择apache或者nginx服务器,这里是2个服务器的介绍: apache:http://baike.baidu.com/view/28283.htmnginx:http://baike.baidu.com/view/926025.htm 引用: 在高并发连接的情况下,Nginx是Apache服务器不错的替代品。Nginx同时也可以作为7层负载均衡服务器来使用。根据我的测试结果,Nginx 0.8.46 + PHP 5.2.14 (FastCGI) 可以承受3万以上的并发连接数,相当于同等环境下Apache的10倍。 这里我们选择nginx,输入1,回车(如果什么都不输入,直接回车,或者输入错误的字符,则默认选择nginx) 然后我们再选择要安装的nginx的版本(如果web服务器选择的是apache,这里提示信息则是要安装apache的版本),总共有1.0.15/1.2.5/1.4.4三个版本可以提供我们选择,这里我们选择1.2.5版本,则输入2,回车(如果什么都不输入,直接回车,或者输入错误的字符,则默认选择1.0.15版本) 然后我们再选择要安装的php的版本,总共有5.3.18/5.4.23/5.5.7三个版本可以提供我们选择,这里我们选择5.5.7版本,则输入3,回车(如果什么都不输入,直接回车,或者输入错误的字符,则默认选择5.3.18版本) 然后我们再选择要安装的mysql的版本,总共有5.1.73/5.5.35/5.6.15三个版本可以提供我们选择,这里我们选择5.1.73版本,则输入1,回车(如果什么都不输入,直接回车,或者输入错误的字符,则默认选择5.1.73版本) 然后可以看到我们刚才选择的版本如下,我们输入y或者Y进行安装。 到这里,我们就正式进入环境安装环节了。你可以泡杯茶休息下。一般这个过程会持续半个小时的样子。 注意: 1、请在网络通畅的情况下进行安装,不要强行终止安装过程(强行关闭安装窗口或者ctrl+c强行终止安装进程),则会导致安装失败,以及出现不可预知的安装异常! 2、安装前,最好能跟系统盘打上快照,如果异常,方便回滚系统。 为了防止断掉和服务器的连接,可以新开一个shell窗口,将鼠标挪到窗口标签栏,右击,如下图所示: 可以看到,新开了一个窗口。 输入top命令 可以看到一直在跳动的系统资源统计。 好了。做完了这一些就让我们静静的等待吧!程序正在自动编译安装服务! 到这个界面说明安装已经结束了,我们可以输入以下命令再次验证是不是已经安装成功: 输入命令 :netstat -tunpl 我们可以看到正在运行状态的服务及端口:9000端口是php进程服务,3306端口是mysql服务,80端口是nginx服务,21端口是ftp服务。 如果看到以上信息,则说明安装没有异常。这里有一点需要注意的是,如果您选择安装的是apache,则没有上面的9000端口。为什么选择安装的是apache,就没有9000端口的php进程服务呢?这是因为nginx+php集成方式与apache+php集成方式不同,感兴趣的朋友可以自己入研究一下,这里就不再介绍。 大家有疑问了。那我怎么登录ftp和mysql呢? 在命令行里输入: cat account.log 看到了没 ftp的用户名是:www mysql的用户名是:root 密码就在屏幕上了! 另外我们也可以cat /alidata/website-info.log中查看到刚才安装软件的版本信息。 好了。所有配置都已经完成了。是不是很简单! 接下来我们可以访问一下一键安装包默认安装的phpwind论坛以及phpmyadmin。 直接在浏览器中输入您的域名或者ip,如果是第一次访问phpwind,则会自动跳转到安装页面。 如果访问的时候卡主,不出来页面,则检查一下防火墙,并自行设置或者关闭防火墙。 点击“接受”进行安装。 然后选择数据库类型为mysql,数据库用户名和数据库密码填写account.log中的用户名和密码。然后根据自己的要求,设置好数据库名、管理员账号和密码等。 填写好信息后,点击下一步完成安装。 接下来我们访问phpmyadmin,在浏览器中键入我们的域名或者ip+phpmyadmin路径,即可访问我们安装的phpmyadmin。 输入mysql用户名以及密码,即可登录。 最后我们就可以在这里面操作管理我们的mysql啦。很简单方便吧。 ---------------------------------------------------------------------- 网站目录:/alidata/www 服务器软件目录:/alidata/server Mysql 目录 /alidata/server/mysql Php目录/alidata/server/php 选择了nginx 那么会有一个nginx 目录在 /alidata/server/nginx/ Nginx 配置文件在/alidata/server/nginx/conf Nginx虚拟主机添加 你可以修改/alidata/server/nginx/conf/vhosts/phpwind.conf 选择了apache那么会有一个httpd 目录在 /alidata/server/httpd apache 配置文件在/alidata/server/httpd/conf apache虚拟主机添加 你可以修改/alidata/server/httpd/conf/vhosts/phpwind.conf 各个服务操作命令汇总: nginx: /etc/init.d/nginx start/stop/restart/reload) apache: /etc/init.d/httpd start/stop/restart/... mysql: /etc/init.d/mysqld start/stop/restart/... php-fpm: /etc/init.d/php-fpm start/stop/restart/... ftp: /etc/init.d/vsftpd start/stop/restart/... 比如启动nginx: /etc/init.d/nginx start 分类: Linux 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6872136.html,如需转载请自行联系原作者
以下是对php中class类的用法进行了详细的总结介绍,需要的朋友可以过来参考下 一:结构和调用(实例化): class className{} ,调用:$obj = new className();当类有构造函数时,还应传入参数。如$obj = new className($v,$v2…); 二:构造函数和析构函数: 1、构造函数用于初始化:使用__construct(),可带参数。 2、但析构函数不能带参数(用于在销去一个类之前执行一些操作或功能)。析构函数用__destruct()做名称。在脚本执行结束时,会销掉内存中的对象,因此可不用析造函数,但有些比如COOKIE等,就应当要用此函数销掉。 知识点:在PHP4中也提供了构造函数,但使用的是与类同名的类方法,在PHP5仍能兼容这种做法,当一个类中没有包含__construct时,会查找与类同名的方法,如果找到,就认为是构造函数,如下: 复制代码代码如下: class test { var $b; function test() { $this->b=5; } function addab($c) { return $this->b+$c; } } $a = new test(); echo $a->addab(4); // 返回 9 3、PHP不会自动调用父类的构造函数(不支持构造函数重载),必须使用parent关键字显式地调用。 复制代码代码如下: class employee{ function __construct()…. } class Manager extents Employee{ function __construct(){ parent::_construct(); echo ‘这个子类的父类构造函数调用了!'; } } 当然也可以调用与该实例没有任何关系的其它类的构造函数。只需在__construct()前加上类名即可。如: otherClassName::__construct(); 类的主家庭成员:属性、方法、常量、静态成员 三、类的属性:有两种方法对类的属性赋值或取值。 1、使用公共作用域public关键词。 2、使用__set()和__get()来分别赋值和取值,前者称为设置方法(setter)或修改方法(mutator),后者称为访问方法(accessor)或获取方法(getter)。建议使用这种方法:优点: A、可在__set()统一进行数据验证。 B、便于统一管理属性。 注意: 第一:__set()和__get()只对私有属性起作用,对于用public定义的属性,它们两个都懒理搭理,如下: 复制代码代码如下: class test{ protected $a=9,$b=2,$c; public $d; function __set($n,$v) { $this->$n = $v+2; } function __get($name) { return $this->$name+2; } } $a = new test(); $a->b =5; echo “<br />”; echo $a->b; 实例只对$a,$b,$c的设置会经过__set和__get过滤与返回,对于$d,就不会起作用。如$a->d=5,再返回还是5。 第二:__set($n,$v)要带两个参数。而__get($n)只能有一个参数。实例: 复制代码代码如下: class test{ private $a=5,$b=6,$c; function __set($n,$v) { if($n=='a'&&$n>0) $this->$n = $v; else $this->$n = $v+2; } function __get($name) { return $this->$name; //如果改为return $this->$name + $this->addab(); 如调用a的值,实际返回的是a+a+b的值。默认为5+5+6=16。 } function addab() { return $this->a + $this->b; } } $e=new test(); $e->a = 11; //注意写法:类的内部用$this->$n即变量的写法,但外部实例要用$e->a的方式。 $e->b = 12; //get 14 $e->k = 22; 类的属性可自由扩展,如上例的k,不管是否用__set,当一个实例建立起来后,可以用$e->newProperty = xx;直接来创造一个属性,但不建议这么做。 四、类的方法:理解成类当中的函数即可。 调用: 1、内部调用:可使用$this->Fanname();或$this->addab()或test::addab(); 2、实例化调用时,用$e->addab();即可。对于在该方法中没有使用$this关键字的,如上例中的: function addab() { return $this->a+$this->b; } 改为: function addab() { return 25; }那在在外部实例调用该方法,也可用“$e::addab();”或“test::addab();” 五、类的常量:如果类的属性理解成类中的变量,那么类的常量和变量是不一样的,其定义方法为: 复制代码代码如下: class test{ private $a; const PI = '3.14′; ….. //在类中调用上面的常量用两种方法,“$this::PI”,或 “类名::PI”,这里就是test::PI,如下: function getvalue(){ return $this->a * $this::PI; //或$this->a * test::PI,用this关键字或类名均可,但都要用双冒号。 } } $e= new test(); $e->PI =5; //注意,这里用 ->只是创造了一个也是名为PI的属性,而不是改变类中的PI常量的值。 echo $e::PI; //这个才是调用类的常量。 常量只能用双冒号::来调用。并且不能更改其值。 在类外部实例化后调用类常量同样也有两种方法。方法为: “$e::PI” 或 “test::PI”,共同点是都要用冒号,不同点是外部不能用this关键字,只能用实例名,但类名::PI是通用的。 六、类的静态成员(静态属性或静态方法): 如果需要创建供所有类的实例共享的字段或方法。就得用静态成员。有两个特征: 1、静态成员是共产主义者,它让脚本上的所有该类的实例调用,但不能借助类的特定实例名调用,而是在类的外部,统一使用“类名::$成员名”的方式调用。而类的内部则统一使用 “self::$成员名”来调用。 2、当每一次新创建实例时,静态成员会从上次创建的实例最后值开始重新计算,而不是类中初始的值开始计算。 3、对于用public定义的静态成员,可以在外部更改它的值。private等则不行。 复制代码代码如下: class test{ public static $v = 0; function __construct(){ self::$v++; } static function getV(){ return self::$v; } } $a = new test(); echo test::getV(); // 返回 1 $b = new test(); echo test::getV(); // 返回 2 test::$v=8; //由于public定义的成员,改变静态成员的值。 $c = new test(); echo test::getV(); // 返回 9 七、关键字:(一)this关键字:用于类的内部指代类的本身。来访问属性或方法或常量,如$this->属性名或方法名。$this::常量名。this还可以用在该类的子类中,来指代本身的属性或方法。 (二)双冒号“::”关键字:用于调用常量、静态成员。 (三)self关键字:在类的内部与双冒号配合调用静态成员,如 self::$staticVar.,在类的内部,不能用$this来调用静态成员。 (四)__toString():在类中使用__toString(),用于将类转成字串并打印类,用处不大:如: 复制代码代码如下: class test{ public $p; public function __toString(){ return var_export($this,TRUE); } } $a=new test(); echo $a; //输出:test::__set_state(array( ‘p' => NULL, )),或写成:echo $a->__toString(); (五)__clone() :当克隆对象时,这个关键字才会发生作用,用于更改克隆时某些值。 (六)__call():方法重载,参下面示例: 复制代码代码如下: class cB{ function __call($method,$n){ if($method=='showVarType'){ if(is_numeric($n[0])){ //不能用$n。要用$n[0]; $this->displayNum(); }else if (is_array($n[0])){ $this->displayArr(); }else{ $this->displayOther(); } } } function displayNum() { echo ‘<h3>这是数字!</h3>'; } function displayArr() { echo ‘<h3>这是数组!</h3>'; } function displayOther() { echo ‘<h3>不是数组也不是数字!</h3>'; } } $x='a'; $y=array(‘a','b'); $b=new cB; $b->showVarType($x); //不是数组也不是数字 $b->showVarType($y); //这是数组 注意,不能在类中定义showVarType()方法,否则代码不能用。 (七)extends:继承: 如class a{} class b extends a{} 类b继承了类a 附:记忆:以后统一在调用方法或属性时用 “-> “,调用常量则用双冒号“::”,不会搞晕。 八、方法和属性的作用域: 共有6种:public(默认,可省略,也等同于php6的var声明),private(私有,也不能由子类使用),protected(私有,但可由子类使用) ,abstract(抽象,参下文),final(阻止在子类中覆盖—也称重载,阻止被继承,用于修饰类名及方法,如final class test{ final function fun(){}} ,但不能用于属性),static(静态) 九:抽象类和抽象方法(abstract——注意:没有所谓抽象属性): 抽象可以理解成父类为子类定义了一个模板或基类。作用域abstract只在父类中声明,但在子类中实现。注意事项: 1、抽象类不能被实例化,只能被子类(具体类)继承后实现。 2、抽象类必须在其子类中实现该抽象类的所有抽象方法。否则会出错。 3、在抽象方法中,只是声明,但不能具体实现:如abstract function gettow(){ return $this->p; }是错的,只能声明这个方法:abstract function gettow();(连方括号{}都不要出现),抽象方法和抽象类主要用于复杂的类层次关系中。该层次关系需要确保每一个子类都包含并重载了某些特定的方法。这也可以通过接口实现 4、属性不能被命名为抽象属性,如abstract $p = 5是错的。 5、只有声明为抽象的类可以声明抽象方法,但如果方法声明为抽象,就不能具体实现。如: 复制代码代码如下: abstract class Employee { abstract function a(…); abstract function b(…); } 以后再对这个父类扩展,组成各种子类(如经理,员工,出纳)。 6、抽象类中,如果要实现具体的方法,不能声明为抽象。这样可能实际意义更大。可以把几个类库中共同的部分提取到抽象类中,其它的类继承抽象类即可。如下: 复制代码代码如下: abstract class BaseShop{ Const TAX=0.06; // 在抽象类中定义常量 public function buy($gid) { // 如果定义为抽象方法abstract function buy()就不能在这里实现主体。 echo(‘你购买了ID为 :'.$gid.'的商品'); } public function sell($gid) { echo(‘你卖了ID为 :'.$gid.'的商品'); } public function view($gid) { echo(‘你查看了ID为 :'.$gid.'的商品'); } } class BallShop extends BaseShop{ var $itme_id = null; public function __construct() { $this->itme_id = 2314; } public function open() { $this->sell($this->itme_id); } public function getTax() { echo printf(‘<h3>平均税率是 %d%%。</h3>',$this::TAX*100); } } $s = new BallShop; $s->open(); //你卖了ID为 :2314的商品 $shop->getTax(); 十:类型提示:注意,类型提示功能只能用于参数为对象的提示,而无法用于为整数,字串,浮点等类型提示。有些类的方法需要传入的参数为所期望的对象类型,可以用下面的方法达到强制实施此替则。要达到类型提示,只要在方法的对象型参数前加一个已存在的类的名称,如:function funname(OtherClassName $otherclassINSName,$c….),注意,OtherClassName必须是存在的类。如下: 复制代码代码如下: class em{ var $k=56; } class test{ function __construct() { echo $this->addab(new em(),2); } function addab(em $j,$c) //这个方法,即可以在内部调用,也可以在外部调用。只要作用域许可。 { return $j->k+$c; } } $a = new test(); $b = new em(); echo $a->addab($b,2); //或 $a->addab(new em(),2); 十一、类的管理: 1、instanceof关键字:用于分析一个对象是否是某一个类的实例或子类或是实现了某个特定的接口:如下例,但要注意: 类名没有任何引号等定界符,否则会出错。如test不能用'test' 复制代码代码如下: class test2{} class test{} class testChilern Extends test{} $a = new test2(); $m = new test(); $i = ($m instanceof test); if($i)echo ‘$m是类test的实例!<br />'; // get this value switch ($a instanceof test){ case true : echo ‘YES<br />'; break; case false : echo ‘No<br />'; //get this value break; } $d=new testChilern(); if($d instanceof test)echo ‘$d是类test的子类!<br />'; // get this value 2、确定类是否存在:boolean class_exists(string class_name): class_exists(‘test'); 3、返回类名:string get_class(object),成功时返回实例的类名,失败则返回FALSE: $a = new test2(); echo get_class($a); //返回 test2 4、了解类的公用属性:array get_class_vars(‘className') ,返回关键数组:包含所有定义的public属性名及其相应的值。这个函数不能用实例名做变量 5、返回类方法:get_class_methods(‘test'); //或: get_class_methods($a);可用实例名做参数,返回包括构造函数在内的所有非私有方法。 6、print_r(get_declared_classes())了解当前PHP版本中所有的类名。PHP5有149个。 7、get_object_vars($a)返回实例中所有公用的属性及其值的关联数组。注意它和get_class_vars()的区别: /* (1) get_object_vars($a)是用实例名做参数,而get_class_vars(‘test')是用类名做参数。 * (2) get_object_vars($a)获得的属性值是实例运行后的值,而get_class_vars(‘test')获得的属性值是类中的初始定义。 * (3) 两者均返回关联数组,且均对未赋值的属性返回NULL的值。如类test中有定义了public $q;则返回Array ( [v] => 5 [q]=>) , */ 8、返回父类的名称:get_parent_class($b);//或get_parent_class(‘test2′); 返回test 9、确定接口是否存在:boolean interface_exists($string interface[,boolean autoload]) 10、确定对象类型: boolean is_a($obj,'className'),当$obj属于CLASSNAME类时,或属于其子类时,返回TRUE,如果$obj与class类型无关则返回FALSE。如:is_a($a,'test') 11、确定是否是某类的子对象:当$b是继承自TEST类时,返回TRUE,否则FALSE。boolean is_subclass_of($b,'test'); 12、确定类或实例中,是否存在某方法。method_exists($a,'getv') //或用method_exists(‘test','getv'),此函数适用于非public定义的作用域的方法。 以上函数实例: 复制代码代码如下: class test{ public $v=2; private $c=5; function __construct(){ $this->v=5; } private function getv(){ return $this->v; } } class test2 extends test{} $a=new test(); $b=new test2(); print_r( get_class_methods(‘test')); //或:print_r( get_class_methods($a)); 均返回:Array ( [0] => __construct [1] => getv ) echo ‘<br />'; print_r( get_class_vars(‘test')); //返回:Array ( [v] => 2 ),和上面不一样,不能用print_r( get_class_methods($a)); echo ‘<br />'; echo get_parent_class($b);//或get_parent_class(‘test2′); 返回test echo ‘<br />'; echo is_a($b,'test');// 返回1 echo ‘<br />'; if(is_subclass_of(‘test2′,'test'))echo ‘是子类!'; //或(is_subclass_of($b,'test')),返回1,当参数1为$a时则返回false, echo ‘<br />'; echo method_exists($a,'getv') //或用method_exists(‘test','getv')返回1,本函数也适用于用private等定义域的方法。 十一、自动加载类库文件: 当类多了以后,比如要在一个文件中载入3个类库文件:a.class.php,b.class.php,c.class.php要用三个require_once(‘classes/a.class.php); require_once(‘classes/b.class.php); require_once(‘classes/c.class.php); 可以用PHP5自动加载的功能来处理:在全局应用配置文件中,定义一个特殊的函数__autoload($class)函数(__autoload并不是一个类的方法,只是单独的函数,和类没有关系): function __autoload($class){ require_once(“classes/$class) } 该函数放哪没有关系,在创建类实例时,也不必去调用这个autoload函数。PHP会自动完成。但务必注意一点:“在调用页面上创建实例所使用的类名称”、和“被调用的文件名”、以及“该文件中的类的名称”3个必须是一样的。这样就不需要去调用__autoload();如果不一样则必须单独调用__autoload(‘c');并给它一个文件名前缀。如: c.class.php文件的代码是: 复制代码代码如下: < ?php class c{ public $m=7; } ?> 这里代码的类名称是c,而文件名也是c, 现在要在index.php调用: 复制代码代码如下: < ?php function __autoload($class){ require_once “$class.class.php”; } $m = new c(); //创建实例调用的类也是c echo $m->m; ?> 此时PHP会自动调用根目录下的c.class.php中的类C。 但如果c.class.php中的代码是: 复制代码代码如下: < ?php class mm{ public $m=7; } ?> 而调用页index.php代码是: 复制代码代码如下: < ?php function __autoload($class){ require_once “$class.class.php”; } # __autoload(‘c'); //如果不加这一行就会出错。 $m = new mm(); echo $m->m; ?> 会出错,提示找不到mm.class.php文件。这时可以加一行__autoload(‘c');但这样就达不到简化代码的目的。 类的家族化扩展:类的高级功能: 一、对象克隆:当克隆一个对象的实例时,其属性初始值继承了被克隆对象的当前值。 复制代码代码如下: class test { public $p=5; function __clone(){ //只在克隆发生时起作用。用于改变在克隆时某些值 $this->p=15; } } $a=new test(); echo $a->p; $a->p=8; //如果没有__clone()方法影响,$b的P值将为8 $b = clone $a; echo $b->p; //15 二、对象继承: 没有被声明为final的类可以被继承,没有被final和private界定的方法也可以继承,没有被private界定的属性也可以继承。当子类继承了父类或超类后,可以直接使用父类或超类(祖父类以及祖父的祖父)的所有允许的方法,属性。 关键:理解构造函数和重载在继承中的特性! (一)构造函数在继承中的特性: 1、当父类有构造函数而子类没有:则子类会在实例化时会自动执行父类的构造函数。这时如果要创建子类的实例,需要引入父类构造函数中所需的参数,否则出错。即使是“子类的子类”如果没有构造函数,也要在创建实例时输入其父类的父类的构造函数所需参数。PHP会从实例所在的子类会向上搜索合造的构造函数,一旦找到就停止,使用该构造函数。而不会再向上搜索,因此:子类本身如果没有构造函数,则以其最靠近的一个超类并且有构造函数的为准。 复制代码代码如下: class cA{ public $name,$age; function __construct($n) { $this->name = $n; $this->age = 25; } function __set($n,$v) { $this->$n = $v; } function __get($n) { return $this->$n; } } class cB extends cA{ function funB1() { echo ‘<h3>Class cB execute success!</h3>'; } } class cC extends cB { function funC1() { echo ‘<h3>Class cC FunC1!</h3>'; } } $b=new cB(‘Jack'); $b->name='John'; echo “$b->name : $b->age”; $b->funB1(); $c=new cC(); //这里会出错,由于cB也没有构造函数,因此再向上以cA为准,需要一个参数。改为$c=new cC(‘David');即可。 echo $c->name(); //David 2、当子类也有构造函数时:这时,不管父类是否有构造函数,都会执行子类自己的构造函数。 如上: 复制代码代码如下: class cB extends cA{ function __construct() { echo ‘<h3>this is Class cB \'s __construct!</h3>'; } function funB1() { echo ‘<h3>Class cB execute success!</h3>'; } } 现在类CB有自己的构造函数时,这时创建实例$b=new cB(‘Jack');参数JACK不会起作用,因为父类CA的构造函数没有得到执行。因此$b->name和$->age就不会初始化值。需要另外赋值$b->name='Jack',$b->age=25; 如果这时要执行父类CA的构造函数,可以这样: 复制代码代码如下: function __construct($n) { parent::__construct($n); // 或:cA::__construct($n); echo ‘<h3>this is Class cB \'s __construct!</h3>'; } 由于parent::__construct($n); 只会向上搜索父类的构造函数,一找到就停止且执行当前找到的构造函数,因此在上面例子中,如果parent::__construct($n)是用在最后一层的类cC中,并且类CB,CA都有构造函数,那么cC的实例只会执行cB的构造函数。不会执行cA。这时,如果CC的实例想都调用CA和CB的构造函数,有两种方法: A、在CB中也加入parent::__construct($n) B、在CC中把构造函数改为: 复制代码代码如下: function __construct($n) { cA::__construct($n); //即:类名::构造函数。 cB::__construct(); echo ‘<h3>this is Class cB \'s __construct!</h3>'; } (二)在子类中调用父类的属性或方法: 1、调用父类方法:在子类中调用父类的方法,有3种方法: $this->ParentFunction(); 或 父类名::ParentFunction(); 或 parent::parentFun(); 2、调用父类属性:只能用$this->ParentProperty; (三)重载: 在子类中,可以定义与父类相同属性或方法,改变父类该属性或方法的值或操作,称做重载。如: calss ParClass{ function pfun(){ ….}} class ChildrenClass extends ParClass{function pfun(){ ….}}} //重载了父类的pfun的方法。 在子类中重载后,优先执行自己重载后的新定义的方法或属性。 也可以在子类中用parent::parentFun();调用父类的方法,但所得到的值是子类自己输入的参数运算值。而不是该方法在父类中运算的值。 三、接口: 接口:interface,可以理解成一组功能的共同规范,最大意义可能就是在多人协作时,为各自的开发规定一个共同的方法名称。 和抽象类中的抽象方法一样: 1、不能在接口中对方法具体实现进行定义。而是由具体类来实现(而抽象类中的非抽象方法可以不必再定义,只有抽象方法和接口是一样要求要在具体类中实现)。 2、和抽象类一样,可以在接口中定义常量,并由具体类直接继承。 3、具体类必须实现抽象类的所有抽象方法(非抽象方法除外),同样,具体类如通过implements实现了接口后,必须完成接口中的所有方法。 接口实现过程:1、定义接口,2、用..implement X,Y,…和具体类对接。 复制代码代码如下: interface Info{ //定义接口 const N=22; public function getage(); public function getname(); } class age implements Info //如要多个接口 class age (extends emJob) implements Info,interB… { public $age=15; public $name='Join'; function getage() { echo “年级是$this->age”; } function getname() { echo “姓名是$this->name”; } function getN(){ echo ‘<h3>在接口中定义的常量N的值是:'.$this::N.' </h3>'; //直接继承接口中的常量值。 } } $age=new age; echo $age::N; //22,直接调用接口中的常量值。 $age->getN(); 关于抽象类和接口类的使用区分:何时用接口,何时用抽象? 1、相关性:当创建的模型由一些紧密相关的对象采用时,用抽象。对于不相关对象采用的功能,用接口。 2、多重继承:PHP类可以继承多个接口,但不能扩展多个抽象类。 3、公共行为实现:抽象类可在其中实现公共的方法,但接口不行。 四、命名空间(PHP6) 类库脚本A.inc.php和脚本B.inc.php中都一个类的名称为 class CNAME,并且这两个文件要在同一个文件如index.php中被调用。这时要用到命名空间。 步聚: 1、打开上面的A和B两个文件,分别在上面的最前面各加一行: namespace SPACEA; 和 namespace SPACEB; 名字自定。 2、在index.php中实例化类时,在类的前面添加命名空间和双冒号做为前缀: include ‘a.inc.php'; include ‘b.inc.php'; $a=new SPACEA::CNAME(); $b=new SPACEB::CNAME(); 这样就不会冲突了。 但在PHP6正式发布前,这个功能还未定下来。 五、实现迭代器和迭代。参《PHP圣经》P142; 六、使用Reflection(反射)API 。简易实例: class a{ …. } $c = new ReflectionClass(‘a'); //PHP 内置类。 echo ‘<pre>'.$c.'</pre>'; 输出类a的结构和内容。参《PHP圣经》P145; 分类: PHP 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6903826.html,如需转载请自行联系原作者
用户URL请求 调用应用入口文件(通常是网站的index.php) 载入框架入口文件(ThinkPHP.php) 记录初始运行时间和内存开销 系统常量判断及定义 载入框架引导类(Think\Think)并执行Think::start方法进行应用初始化 设置错误处理机制和自动加载机制 调用Think\Storage类进行存储初始化(由STORAGE_TYPE常量定义存储类型) 部署模式下如果存在应用编译缓存文件则直接加载(直接跳转到步骤22) 读取应用模式(由APP_MODE常量定义)的定义文件(以下以普通模式为例说明) 加载当前应用模式定义的核心文件(普通模式是 ThinkPHP/Mode/common.php) 加载惯例配置文件(普通模式是 ThinkPHP/Conf/convention.php) 加载应用配置文件(普通模式是 Application/Common/Conf/config.php) 加载系统别名定义 判断并读取应用别名定义文件(普通模式是 Application/Common/Conf/alias.php) 加载系统行为定义 判断并读取应用行为定义文件(普通模式是 Application/Common/Conf/tags.php) 加载框架底层语言包(普通模式是 ThinkPHP/Lang/zh-cn.php) 如果是部署模式则生成应用编译缓存文件 加载调试模式系统配置文件(ThinkPHP/Conf/debug.php) 判断并读取应用的调试配置文件(默认是 Application/Common/Conf/debug.php) 判断应用状态并读取状态配置文件(如果APP_STATUS常量定义不为空的话) 检测应用目录结构并自动生成(如果CHECK_APP_DIR配置开启并且RUNTIME_PATH目录不存在的情况下) 调用Think\App类的run方法启动应用 应用初始化(app_init)标签位侦听并执行绑定行为 判断并加载动态配置和函数文件 调用Think\Dispatcher::dispatch方法进行URL请求调度 自动识别兼容URL模式和命令行模式下面的$_SERVER['PATH_INFO']参数 检测域名部署以及完成模块和控制器的绑定操作(APP_SUB_DOMAIN_DEPLOY参数开启) 分析URL地址中的PATH_INFO信息 获取请求的模块信息 检测模块是否存在和允许访问 判断并加载模块配置文件、别名定义、行为定义及函数文件 判断并加载模块的动态配置和函数文件 模块的URL模式判断 模块的路由检测(URL_ROUTER_ON开启) PATH_INFO处理(path_info)标签位侦听并执行绑定行为 URL后缀检测(URL_DENY_SUFFIX以及URL_HTML_SUFFIX处理) 获取当前控制器和操作,以及URL其他参数 URL请求调度完成(url_dispatch)标签位侦听并执行绑定行为 应用开始(app_begin)标签位侦听并执行绑定行为 调用SESSION_OPTIONS配置参数进行Session初始化(如果不是命令行模式) 根据请求执行控制器方法 如果控制器不存在则检测空控制器是否存在 控制器开始(action_begin)标签位侦听并执行绑定行为 默认调用系统的ReadHtmlCache行为读取静态缓存(HTML_CACHE_ON参数开启) 判断并调用控制器的_initialize初始化方法 判断操作方法是否存在,如果不存在则检测是否定义空操作方法 判断前置操作方法是否定义,有的话执行 Action参数绑定检测,自动匹配操作方法的参数 如果有模版渲染(调用控制器display方法) 视图开始(view_begin)标签位侦听并执行绑定行为 调用Think\View的fetch方法解析并获取模版内容 自动识别当前主题以及定位模版文件 视图解析(view_parse)标签位侦听并执行绑定行为 默认调用内置ParseTemplate行为解析模版(普通模式下面) 模版引擎解析模版内容后生成模版缓存 模版过滤替换(template_filter)标签位侦听并执行绑定行为 默认调用系统的ContentReplace行为进行模版替换 输出内容过滤(view_filter)标签位侦听并执行绑定行为 默认调用系统的WriteHtmlCache行为写入静态缓存(HTML_CACHE_ON参数开启) 调用Think\View类的render方法输出渲染内容 视图结束(view_end)标签位侦听并执行绑定行为 判断后置操作方法是否定义,有的话执行 控制器结束(action_end)标签位侦听并执行绑定行为 应用结束(app_end)标签位侦听并执行绑定行为 执行系统的ShowPageTrace行为(SHOW_PAGE_TRACE参数开启并且不是AJAX请求) 日志信息存储写入 如果你绑定了更多的应用行为的话,流程可能会更加复杂。 如果是部署模式下面的第二次请求的话,上面的流程中的步骤10~21是可以省略的。 分类: PHP 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6952091.html,如需转载请自行联系原作者
使用UIView动画函数实现转场动画——双视图 + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion; 参数说明: –duration:动画的持续时间 –options:转场动画的类型 –animations:将改变视图属性的代码放在这个block中 –completion:动画结束后,会自动调用这个block 方法调用完毕后,相当于执行了下面两句代码: // 添加toView到父视图 [fromView.superview addSubview:toView]; // 把fromView从父视图中移除 [fromView removeFromSuperview]; 具体实例如下: 实现功能:创建两个子视图view1和view2,分别设置不同的背景颜色,并把它们添加到父视图中,然后创建触摸点击手势, 每次触摸屏幕时,两个视图交替切换显示,即实现转场动画。 代码如下: //声明属性 #import "ViewController.h" @interface ViewController () @property (strong,nonatomic)UIView *view1; @property (strong,nonatomic)UIView *view2; @end //初始化,创建两个子视图,同时创建触摸点击手势并添加手势事件 - (void)viewDidLoad { [super viewDidLoad]; //初始化 self.view1 = [[UIView alloc]initWithFrame:self.view.frame]; self.view1.backgroundColor = [UIColor redColor]; //view1背景色为红色 [self.view addSubview:self.view1]; self.view2 = [[UIView alloc]initWithFrame:self.view.frame]; self.view2.backgroundColor = [UIColor greenColor];//view2背景色为绿色 [self.view addSubview:self.view2]; //添加tap手势 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)]; tap.numberOfTapsRequired = 1; tap.numberOfTouchesRequired = 1; [self.view addGestureRecognizer:tap]; } //处理触摸点击手势事件,在block函数中实现双视图转场动画 #pragma mark -tap手势事件 -(void)tap:(UITapGestureRecognizer *)sender { //通过判断视图的父视图是否为空,可以知道当前视图是不是正在显示 if (self.view2.superview == nil) { //通过block函数切换子视图(设置动画过渡类型为翻书效果) [UIView transitionFromView:self.view1 toView:self.view2 duration:1.0f options:UIViewAnimationOptionTransitionCurlUp completion:nil];; } else { //通过block函数切换子视图(设置动画过渡类型为翻书效果) [UIView transitionFromView:self.view2 toView:self.view1 duration:1.0f options:UIViewAnimationOptionTransitionCurlUp completion:nil]; } } 演示结果如下: 开始时: 点击后: 动画结束后: 还可以接着点击,会变为绿色视图,就不一一演示了。 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: iOS高级 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4886689.html,如需转载请自行联系原作者
一、const指针 1、const int* p和int const* p:两者意义是相同的。指向的内容是只读数据,不可以q改变;但是指向的地址可以改变。 2、int* const p:必须先对指针初始化,而且指向的地址是只读的,不可以再被改变;但是指向的内容可以改变。 3、const int* const p:既不可以改变指针指向的地址,也不可以改变指针指向的内容。 二、指针数组:元素类型全是指针 类型名称*数组名[数组长度] 例如: char* pc[10]:字符指针数组,常用来可以表示一个字符串 三、数组指针:指针指向数组名 类型名称(*指针名)[数组长度] 例如: int a[5] = {1,2,3,4,5}; int (*temp)[5] = &a; //temp指向的是整个数组 注意: 1、定义指针时()不能丢掉,因为[]优先级比*高,若丢掉,就会变成指针数组。例如:int *temp[4]//数组4个元素都是int*。 2、数组长度、元素类型必须与指针定义时给出的长度、类型相同。 四、函数指针 数据类型(*指针变量名称)(形式参数列表) 本质:函数放在代码区,函数指针指向代码区,通过函数指针可以访问代码区中的内容。括号()不可以省。。。 例如:float (*p)(float a,float y):float类型指针函数 float* p(float x,float y):函数p返回值为float指针类型 五、总结数组与指针的区别: 1、指针的本质是一个与地址相关的复合类型,它的值是数据存放的位置(地址);数组的本质则是一系列的变量。2、数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是"可变",所以我们常用指针来操作动态内存。3、当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。 因为第三条,当把数组名作为函数的形参进行传递时,该效果等同于传递一个同类型的指针。直接在程序当中调用 函数名(数组名)即可。 程序猿神奇的手,每时每刻,这双手都都在改变着世界的交互方式! 分类: C 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4887949.html,如需转载请自行联系原作者
可以通过设置MKMapView的mapViewType设置地图类型 MKMapTypeStandard 普通地图 MKMapTypeSatellite 卫星云图 MKMapTypeHybrid 普通地图覆盖于卫星云图之上 @property (nonatomic) MKMapType mapType; //地图类型 @property (nonatomic) MKCoordinateRegion region; //位置区域 @property (nonatomic) CLLocationCoordinate2D centerCoordinate;//位置经纬度 //位置区域范围(0~1) typedef struct { CLLocationDegrees latitudeDelta; CLLocationDegrees longitudeDelta; } MKCoordinateSpan; //位置经纬度 typedef struct { CLLocationDegrees latitude; CLLocationDegrees longitude; } CLLocationCoordinate2D; //位置区域 typedef struct { CLLocationCoordinate2D center; MKCoordinateSpan span; } MKCoordinateRegion; //在地图上设置显示区域 - (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated; - (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated; MapView会将一些事件传递给它的代理(遵守MKMapViewDelegate协议),代理方法如下: mapViewWillStartLoadingMap: 当地图界面将要加载时调用 mapView:viewForAnnotation: 当地图上有一些动画效果展示\加载时调用 mapViewWillStartLocatingUser:当准备进行一个位置定位时调用 mapView:regionDidChangeAnimated: 当显示的区域发生变化时调用 mapView:didUpdateUserLocation:当用户位置发生变化时调用 具体实例如下: 1、导入CoreLocation/Mapkit框架 2、拖入一个UIMapView控件到控制器中 3、在ViewController.m文件中导入必要的库文件 4、将MapView关联为IBOutLet属性,同时让ViewController类实现地图协议 5、代码实现功能 //显示地图 - (void)viewDidLoad { [super viewDidLoad]; //设置地图的属性 self.mapView.mapType = MKMapTypeHybrid; //设置区域的经纬度坐标 CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(40, 116);//(经度、纬度) //设置经纬度范围(越小越精确) MKCoordinateSpan span = MKCoordinateSpanMake(0.1, 0.1); //设置地图显示的区域 self.mapView.region = MKCoordinateRegionMake(coordinate, span); //设置地图的代理 self.mapView.delegate = self; } //实现地图协议 #pragma mark -mapView的方法 #pragma mark -地图开始加载 -(void)mapViewWillStartLoadingMap:(MKMapView *)mapView { NSLog(@"开始加载地图"); } #pragma mark -地图加载完毕 -(void)mapViewDidFinishLoadingMap:(MKMapView *)mapView { NSLog(@"地图加载完毕"); } #pragma mark -地图区域将要改变 -(void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { MKCoordinateRegion region = mapView.region; NSLog(@"经度:%f,纬度:%f",region.center.latitude,region.center.longitude); } #pragma mark -地图区域已经改变 -(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { MKCoordinateRegion region = mapView.region; NSLog(@"经度:%f,纬度:%f",region.center.latitude,region.center.longitude); } 演示结果截图: 加载调用了代理方法,输出为: 2015-10-19 21:50:19.241 05-mapkit-MapView[7278:360386] 开始加载地图 2015-10-19 21:50:19.261 05-mapkit-MapView[7278:360386] 地图加载完毕 地图显示: 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: iOS高级 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4893237.html,如需转载请自行联系原作者
在尖括号里写一个名字来创建一个泛型函数或者类型 例如<T>、<Type> 可以创建泛型类、枚举和结构体 在类型后使用where来指定一个需求列表。例如,要限定实现一个协议的类型,需要限定两个类型要相同,或者限定一个类必须有一个特定的父类 先给一个具体举例如下: //泛型函数 func repeat<ItemType>(item:ItemType,times:Int)->[ItemType]{ var results:[ItemType] = [ItemType]() for i in 0..<times{ results.append(item) } return results } //第一个参数可以接收任意类型的数据 repeat("Tom", 4) //字符串 repeat(10, 5) //整数 repeat((1,2,3,4), 3) //元组 显示结果: swift泛型具体介绍: http://www.cocoachina.com/newbie/basic/2014/0612/8790.html 泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型。它可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。 泛型是 Swift 强大特征中的其中一个,许多 Swift 标准库是通过泛型代码构建出来的。事实上,泛型的使用贯穿了整本语言手册,只是你没有发现而已。例如,Swift 的数组和字典类型都是泛型集。你可以创建一个Int数组,也可创建一个String数组,或者甚至于可以是任何其他 Swift 的类型数据数组。同样的,你也可以创建存储任何指定类型的字典(dictionary),而且这些类型可以是没有限制的。 泛型所解决的问题 这里是一个标准的,非泛型函数swapTwoInts,用来交换两个Int值: func swapTwoInts(inout a: Int, inout b: Int) let temporaryA = a a = b b = temporaryA } 这个函数使用写入读出(in-out)参数来交换a和b的值,请参考写入读出参数。 swapTwoInts函数可以交换b的原始值到a,也可以交换a的原始值到b,你可以调用这个函数交换两个Int变量值: var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // 输出 "someInt is now 107, and anotherInt is now 3" swapTwoInts函数是非常有用的,但是它只能交换Int值,如果你想要交换两个String或者Double,就不得不写更多的函数,如 swapTwoStrings和swapTwoDoublesfunctions,如同如下所示: func swapTwoStrings(inout a: String, inout b: String) { let temporaryA = a a = b b = temporaryA } func swapTwoDoubles(inout a: Double, inout b: Double) { let temporaryA = a a = b b = temporaryA } 你可能注意到 swapTwoInts、 swapTwoStrings和swapTwoDoubles函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是Int、String和Double。 但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。) 注意: 在所有三个函数中,a和b的类型是一样的。如果a和b不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个String类型的变量和一个Double类型的变量互相交换值。如果一定要做,Swift 将报编译错误。 泛型函数 泛型函数可以工作于任何类型,这里是一个上面swapTwoInts函数的泛型版本,用于交换两个值: func swapTwoValues<T>(inout a: T, inout b: T) { let temporaryA = a a = b b = temporaryA } swapTwoValues函数主体和swapTwoInts函数是一样的,它只在第一行稍微有那么一点点不同于swapTwoInts,如下所示: func swapTwoInts(inout a: Int, inout b: Int) func swapTwoValues<T>(inout a: T, inout b: T) 这个函数的泛型版本使用了占位类型名字(通常此情况下用字母T来表示)来代替实际类型名(如In、String或Doubl)。占位类型名没有提示T必须是什么类型,但是它提示了a和b必须是同一类型T,而不管T表示什么类型。只有swapTwoValues函数在每次调用时所传入的实际类型才能决定T所代表的类型。 另外一个不同之处在于这个泛型函数名后面跟着的展位类型名字(T)是用尖括号括起来的()。这个尖括号告诉 Swift 那个T是swapTwoValues函数所定义的一个类型。因为T是一个占位命名类型,Swift 不会去查找命名为T的实际类型。 swapTwoValues函数除了要求传入的两个任何类型值是同一类型外,也可以作为swapTwoInts函数被调用。每次swapTwoValues被调用,T所代表的类型值都会传给函数。 在下面的两个例子中,T分别代表Int和String: var someInt = 3 var anotherInt = 107 swapTwoValues(&someInt, &anotherInt) // someInt is now 107, and anotherInt is now 3 var someString = "hello" var anotherString = "world" swapTwoValues(&someString, &anotherString) // someString is now "world", and anotherString is now "hello" 注意:上面定义的函数swapTwoValues是受swap函数启发而实现的。swap函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似swapTwoValues函数的功能,你可以使用已存在的交换函数swap函数。 类型参数 在上面的swapTwoValues例子中,占位类型T是一种类型参数的示例。类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(如)。 一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如swapTwoValues函数中的参数a和b),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面swapTwoValues例子中,当函数第一次被调用时,T被Int替换,第二次调用时,被String替换。)。 你可支持多个类型参数,命名在尖括号中,用逗号分开。 命名类型参数 在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的swapTwoValues泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母T来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。 如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为KeyType和ValueType,用来记住它们在你的泛型代码中的作用。 注意:请始终使用大写字母开头的驼峰式命名法(例如T和KeyType)来给类型参数命名,以表明它们是类型的占位符,而非类型值。 泛型类型 通常在泛型函数中,Swift 允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同Array和Dictionary的用法。 这部分向你展示如何写一个泛型集类型--Stack(栈)。一个栈是一系列值域的集合,和Array(数组)类似,但其是一个比 Swift 的Array类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同push一个新值进栈)。同样的一个栈也只能从末端移除项(如同pop一个值出栈)。 注意:栈的概念已被UINavigationController类使用来模拟试图控制器的导航结构。你通过调用UINavigationController的pushViewController:animated:方法来为导航栈添加(add)新的试图控制器;而通过popViewControllerAnimated:的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的后进先出方式来管理集合,堆栈都是最实用的模型。 下图展示了一个栈的压栈(push)/出栈(pop)的行为: 1、现在有三个值在栈中; 2、第四个值“pushed”到栈的顶部; 3、现在有四个值在栈中,最近的那个在顶部; 4、栈中最顶部的那个项被移除,或称之为“popped”; 5、移除掉一个值后,现在栈又重新只有三个值。 这里展示了如何写一个非泛型版本的栈,Int值型的栈: struct IntStack { var items = [Int]() mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } } 这个结构体在栈中使用一个Array性质的items存储值。Stack提供两个方法:push和pop,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为他们需要修改(或转换)结构体的items数组。 上面所展现的IntStack类型只能用于Int值,不过,其对于定义一个泛型Stack类(可以处理任何类型值的栈)是非常有用的。 这里是一个相同代码的泛型版本: struct Stack<T> { var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } 注意到Stack的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际Int类型。这种类型参数包含在一对尖括号里(<T>),紧随在结构体名字后面。 T定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,T在如下三个地方被用作节点: - 创建一个名为items的属性,使用空的T类型值数组对其进行初始化; - 指定一个包含一个参数名为item的push方法,该参数必须是T类型; - 指定一个pop方法的返回值,该返回值将是一个T类型值。 当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个Stack实例,同创建Array和Dictionary一样: var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") stackOfStrings.push("cuatro") // 现在栈已经有4个string了 下图将展示stackOfStrings如何push这四个值进栈的过程: 从栈中pop并移除值"cuatro": let fromTheTop = stackOfStrings.pop() // fromTheTop is equal to "cuatro", and the stack now contains 3 strings 下图展示了如何从栈中pop一个值的过程: 由于Stack是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同Array和Dictionary。 类型约束 swapTwoValues函数和Stack类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。 例如,Swift 的Dictionary类型对作用于其键的类型做了些限制。在字典的描述中,字典的键类型必须是可哈希,也就是说,必须有一种方法可以使其是唯一的表示。Dictionary之所以需要其键是可哈希是为了以便于其检查其是否包含某个特定键的值。如无此需求,Dictionary即不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。 这个需求强制加上一个类型约束作用于Dictionary的键上,当然其键类型必须遵循Hashable协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如String,Int, Double和 Bool)默认都是可哈希。 当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如可哈希具有的类型特征是根据他们概念特征来界定的,而不是他们的直接类型特征。 类型约束语法 你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同): func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // function body goes here } 上面这个假定函数有两个类型参数。第一个类型参数T,有一个需要T必须是SomeClass子类的类型约束;第二个类型参数U,有一个需要U必须遵循SomeProtocol协议的类型约束。 类型约束行为 这里有个名为findStringIndex的非泛型函数,该函数功能是去查找包含一给定String值的数组。若查找到匹配的字符串,findStringIndex函数返回该字符串在数组中的索引值(Int),反之则返回nil: func findStringIndex(array: [String], valueToFind: String) -> Int? { for (index, value) in enumerate(array) { if value == valueToFind { return index } } return nil } findStringIndex函数可以作用于查找一字符串数组中的某个字符串: let strings = ["cat", "dog", "llama", "parakeet", "terrapin"] if let foundIndex = findStringIndex(strings, "llama") { println("The index of llama is \(foundIndex)") } // 输出 "The index of llama is 2" 如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数findIndex,用某个类型T值替换掉提到的字符串。 这里展示如何写一个你或许期望的findStringIndex的泛型版本findIndex。请注意这个函数仍然返回Int,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明: func findIndex<T>(array: [T], valueToFind: T) -> Int? { for (index, value) in enumerate(array) { if value == valueToFind { return index } } return nil } 上面所写的函数不会编译。这个问题的位置在等式的检查上,“if value == valueToFind”。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型T,当你试图编译这部分代码时估计会出现相应的错误。 不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个Equatable协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持Equatable协议。 任何Equatable类型都可以安全的使用在findIndex函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个Equatable类型约束作为类型参数定义的一部分: func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? { for (index, value) in enumerate(array) { if value == valueToFind { return index } } return nil } findIndex中这个单个类型参数写做:T: Equatable,也就意味着“任何T类型都遵循Equatable协议”。 findIndex函数现在则可以成功的编译过,并且作用于任何遵循Equatable的类型,如Double或String: let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) // doubleIndex is an optional Int with no value, because 9.3 is not in the array let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") // stringIndex is an optional Int containing a value of 2 关联类型 当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为typealias关键字。 关联类型行为 这里是一个Container协议的例子,定义了一个ItemType关联类型: protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } Container协议定义了三个任何容器必须支持的兼容要求: 必须可能通过append方法添加一个新item到容器里; 必须可能通过使用count属性获取容器里items的数量,并返回一个Int值; 必须可能通过容器的Int索引值下标可以检索到每一个item。 这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循Container类型所必须支持的功能点。一个遵循的类型也可以提供其他额外的功能,只要满足这三个条件。 任何遵循Container协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。 为了定义这三个条件,Container协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。Container协议需要指定任何通过append方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。 为了达到此目的,Container协议声明了一个ItemType的关联类型,写作typealias ItemType。The protocol does not define what ItemType is an alias for—that information is left for any conforming type to provide(这个协议不会定义ItemType是遵循类型所提供的何种信息的别名)。尽管如此,ItemType别名支持一种方法识别在一个容器里的items类型,以及定义一种使用在append方法和下标中的类型,以便保证任何期望的Container的行为是强制性的。 这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议: struct IntStack: Container { // original IntStack implementation var items = [Int]() mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } // conformance to the Container protocol typealias ItemType = Int mutating func append(item: Int) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Int { return items[i] } } IntStack类型实现了Container协议的所有三个要求,在IntStack类型的每个包含部分的功能都满足这些要求。 此外,IntStack指定了Container的实现,适用的ItemType被用作Int类型。对于这个Container协议实现而言,定义 typealias ItemType = Int,将抽象的ItemType类型转换为具体的Int类型。 感谢Swift类型参考,你不用在IntStack定义部分声明一个具体的Int的ItemType。由于IntStack遵循Container协议的所有要求,只要通过简单的查找append方法的item参数类型和下标返回的类型,Swift就可以推断出合适的ItemType来使用。确实,如果上面的代码中你删除了 typealias ItemType = Int这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。 你也可以生成遵循Container协议的泛型Stack类型: struct Stack<T>: Container { // original Stack<T> implementation var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformance to the Container protocol mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } 这个时候,占位类型参数T被用作append方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的ItemType的T的合适类型。 扩展一个存在的类型为一指定关联类型 在使用扩展来添加协议兼容性中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。 Swift的Array已经提供append方法,一个count属性和通过下标来查找一个自己的元素。这三个功能都达到Container协议的要求。也就意味着你可以扩展Array去遵循Container协议,只要通过简单声明Array适用于该协议而已。如何实践这样一个空扩展,在使用扩展来声明协议的采纳中有描述这样一个实现一个空扩展的行为: extension Array: Container {} 如同上面的泛型Stack类型一样,Array的append方法和下标保证Swift可以推断出ItemType所使用的适用的类型。定义了这个扩展后,你可以将任何Array当作Container来使用。 Where 语句 类型约束中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。 对于关联类型的定义需求也是非常有用的。你可以通过这样去定义where语句作为一个类型参数队列的一部分。一个where语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个where语句,通过紧随放置where关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。 下面的列子定义了一个名为allItemsMatch的泛型函数,用来检查是否两个Container单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为true的Boolean值,反之,则相反。 这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但他们确实拥有相同类型的元素。这个需求通过一个类型约束和where语句结合来表示: func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // check that both containers contain the same number of items if someContainer.count != anotherContainer.count { return false } // check each pair of items to see if they are equivalent for i in 0..someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // all items match, so return true return true } 这个函数用了两个参数:someContainer和anotherContainer。someContainer参数是类型C1,anotherContainer参数是类型C2。C1和C2是容器的两个占位类型参数,决定了这个函数何时被调用。 这个函数的类型参数列紧随在两个类型参数需求的后面: C1必须遵循Container协议 (写作 C1: Container)。 C2必须遵循Container协议 (写作 C2: Container)。 C1的ItemType同样是C2的ItemType(写作 C1.ItemType == C2.ItemType)。 C1的ItemType必须遵循Equatable协议 (写作 C1.ItemType: Equatable)。 第三个和第四个要求被定义为一个where语句的一部分,写在关键字where后面,作为函数类型参数链的一部分。 这些要求意思是: someContainer是一个C1类型的容器。 anotherContainer是一个C2类型的容器。 someContainer和anotherContainer包含相同的元素类型。 someContainer中的元素可以通过不等于操作(!=)来检查它们是否彼此不同。 第三个和第四个要求结合起来的意思是anotherContainer中的元素也可以通过 != 操作来检查,因为他们在someContainer中元素确实是相同的类型。 这些要求能够使allItemsMatch函数比较两个容器,即便他们是不同的容器类型。 allItemsMatch首先检查两个容器是否拥有同样数目的items,如果他们的元素数目不同,没有办法进行匹配,函数就会false。 检查完之后,函数通过for-in循环和半闭区间操作(..)来迭代someContainer中的所有元素。对于每个元素,函数检查是否someContainer中的元素不等于对应的anotherContainer中的元素,如果这两个元素不等,则这两个容器不匹配,返回false。 如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回true。 这里演示了allItemsMatch函数运算的过程: var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") var arrayOfStrings = ["uno", "dos", "tres"] if allItemsMatch(stackOfStrings, arrayOfStrings) { println("All items match.") } else { println("Not all items match.") } // 输出 "All items match." 上面的例子创建一个Stack单例来存储String,然后压了三个字符串进栈。这个例子也创建了一个Array单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但他们都遵循Container协议,而且他们都包含同样的类型值。你因此可以调用allItemsMatch函数,用这两个容器作为它的参数。在上面的例子中,allItemsMatch函数正确的显示了所有的这两个容器的items匹配。 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4905273.html,如需转载请自行联系原作者
SQL Server里面的生成SQL脚本,只会包含数据库及表的字段结构,而不会包含表的数据,也就是SQL脚本里面只有Create database,Create table 这样的语句,没有insert into。那么我们怎么样才能导出数据呢? SQL Server并不包含这个功能,只能靠第三方的代码了。 用这个存储过程可以实现: CREATE PROCEDURE dbo.UspOutputData @tablename sysname AS declare @column varchar(1000) declare @columndata varchar(1000) declare @sql varchar(4000) declare @xtype tinyint declare @name sysname declare @objectId int declare @objectname sysname declare @ident int set nocount on set @objectId=object_id(@tablename) if @objectId is null -- 判斷對象是否存在 begin print 'The object not exists' return end set @objectname=rtrim(object_name(@objectId)) if @objectname is null or charindex(@objectname,@tablename)=0 --此判断不严密 begin print 'object not in current database' return end if OBJECTPROPERTY(@objectId,'IsTable') < > 1 -- 判斷對象是否是table begin print 'The object is not table' return end select @ident=status&0x80 from syscolumns where id=@objectid and status&0x80=0x80 if @ident is not null print 'SET IDENTITY_INSERT '+@TableName+' ON' declare syscolumns_cursor cursor for select c.name,c.xtype from syscolumns c where c.id=@objectid order by c.colid open syscolumns_cursor set @column='' set @columndata='' fetch next from syscolumns_cursor into @name,@xtype while @@fetch_status < >-1 begin if @@fetch_status < >-2 begin if @xtype not in(189,34,35,99,98) --timestamp不需处理,image,text,ntext,sql_variant 暂时不处理 begin set @column=@column+case when len(@column)=0 then'' else ','end+@name set @columndata=@columndata+case when len(@columndata)=0 then '' else ','','','end +case when @xtype in(167,175) then '''''''''+'+@name+'+''''''''' --varchar,char when @xtype in(231,239) then '''N''''''+'+@name+'+''''''''' --nvarchar,nchar when @xtype=61 then '''''''''+convert(char(23),'+@name+',121)+''''''''' --datetime when @xtype=58 then '''''''''+convert(char(16),'+@name+',120)+''''''''' --smalldatetime when @xtype=36 then '''''''''+convert(char(36),'+@name+')+''''''''' --uniqueidentifier else @name end end end fetch next from syscolumns_cursor into @name,@xtype end close syscolumns_cursor deallocate syscolumns_cursor set @sql='set nocount on select ''insert '+@tablename+'('+@column+') values(''as ''--'','+@columndata+','')'' from '+@tablename print '--'+@sql exec(@sql) if @ident is not null print 'SET IDENTITY_INSERT '+@TableName+' OFF' GO 使用方法为: 在查询分析器以“文本显示结果”方法执行 exec UspOutputData 你的表名 然后将运行后的结果存成.sql,加上用SQL Server生成的数据库脚本就可以了。 缺点和问题: 得到导出数据的语句,但image,text,ntext,sql_variant 列不出现在语句,以后改进。 原文地址:http://www.cnblogs.com/lqb/archive/2009/08/21/1551365.html 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/08/21/1551443.html,如需转载请自行联系原作者
无故自动关闭停止已经不是罕见的事情了,处理这个问题是让我很头痛的事情,遇到这个问题不太可能一次性解决,多数都是用排除法一个个测试排除错误,最终找到那个错误命令。最近我的服务器遇到了这个问题,我很无奈,我很急,客户也很着急,每天IIS都要自动停止2次以上,我总是怀疑是进程池问题,此文章是针对IIS进程池解决办法,如果你遇到了死循环代码,或者其他非进程池,那此文章不太适合你了 网络上有关iis的问题和相关解决方案,多不胜搜,但很多都比较零散,没有系统的解决方案;另外,有些解决方法,似是而非,不能找到其中的问题关键点,本人平时对于服务器的应用上也有点实践,因此,今天稍稍总结一点平时遇到地问题和解决方法,特别是对iis的特殊权限引起问题、iis应用程序池假死问题和比较罕见的iis重启命令和自动重启办法。其它相关问题,继续关注本博。一、2003应用程序池自动死了,不能恢复了,一直出现 Service Unavailable 常见方法如下。 1:没有打SP1补丁的时候会出现这个IIS6.0假死问题,但现在微软都在自动更新里面出补丁了,一般你打好最新补丁后是不会出现此问题了。(所以现在的IIS假死与这个关系不是很大) 2:从IIS6.0开始CPU资源都在应用池里面限制了,不象以前的IIS.5。所以假死的池的缘故就是池被拉死,你在网站打不开的时候可以看到你的某个应用池是禁用的,上面出现一个红叉。你鼠标右键启动网站又会自动恢复。 这个原因:大概是以下几个因数造成的。 (1):你限制了应用池的资源,限制得太小 比如:50这样或更少更多一点,这个时候如果你这个池下面的网站占用CPU太高,比如超过50% 那么5分钟后他就自动死了,手工默认建立的应用池默认是超过资源不操作。 出现上面这个情况解决方法:1:不限制CPU资源,(这个是不可取的,不限制资源,有的程序有BUG占用资源厉害了的,服务器都会被拉死,你可能都无法操作服务器。)2:在超过资源那里选择关闭,这个关闭默认是失败5次,90秒内恢复,一般默认就可。网站能自动恢复,这个关闭:不是永久关闭,意思是超过资源关闭,然后在某时间内自动恢复池。不操作就是不恢复,这个是很多人的误区。 (2):内存限制 在IIS6.0应用池上面有虚拟内存和最大内存限制,如果你设置了这个。那么网站访问量大了 也会出现假死,所以不建议设置这里。默认就可。 3:就是服务器自身内存太小,网站运行当然需要使用到内存了,当内存不够的时候应用池也会死掉变成禁用。那么只有等内存全部释放出来才能恢复应用池了。出现这个情况:那么你就要考虑加内存或者检查到底是什么程序占用了内存了。比如MSSQL数据库,这个可是吃内存得大户啊,最好别和WEB服务器同时一个服务器上。很多人用1G内存做 2003系统,2003NET结构是很占用内存的,所以做服务器选2003还得把内存加到2G或更高才好。 内存不够上面 2点讲到的,是没办法操作了,也无法自动恢复。 4:就是ACCESS数据库太大或查询太多,这个也会出现把IIS拉死,解决方法;修复ACCESS数据库,或尽量少用ACCESS数据库,升级至sqlserver数据库;或者在技术方面革新,像现在有些网站系统,风讯、动易等cms;pjblog、zblog等博客程序,都支持生成静态功能. 5:不同网站用不同应用池:根据你自己实际情况而定,站点大的最好独立一个应用池,限制他的资源超过了自动回收,看上面(1)讲到的,这样就不影响其他站点。中型站点:多个网站共用一个应用池,比如5个站点用一个池,设置他资源时间等等。这样他们就算超资源了也不影响其他应用池的网站。 6:设置回收时间:很多人以为设置回收池越短越好,其实是错误的,每次回收当然是把内存回收回来了,但加重了一次服务器的负担,当服务器比较繁忙的时候,有可能导致其他应用池死。所以建议设置共1000就行了。其他独立池按照他网站流量而设置 可以设置600 也行,共用的不建议设置太短。 7:网站后台过不了多久自动退出又要重新登陆:这个情况就是你设置回收时间太短了,按照 6点设置吧。 不要设置什么20分、30分这样的,这样不好的。另外一个原因就是和站的响应设置时间有关,设置得稍长些。 8:windows 2003系统iis6访问本机的站点时提示“Service Unavailable”; 查看iis的应用程序池,状况提示为:未指定错误,同时应用程序池自动停止运行; 用事件查看器查看系统错误日志,发现如下提示: ----------------------------------- 应用程序-特定 权限设置未将 COM 服务器应用程序(CLSID 为 {A9E69610-B80D-11D0-B9B9-00A0C922E750} )的 本地 激活 权限授予用户 NT AUTHORITY\NETWORK SERVICE SID (S-1-5-20)。可以使用组件服务管理工具修改此安全权限。 解决方法,给NETWORK SERVICE 加上访问iis服务的权限,具体方法如下: 点击“开始”-“控制面板”-“管理工具”-“组件服务”-“计算机”-“我的电脑”-“DCOM”选项, 选择其下的“IIS ADMIN SERVICE”,右健选择“属性”,找到“安全”,在“启动和激活权限”中编辑“自定义”,添加帐号“NETWORK SERVICE ”,给该帐号赋予“本地启动”和“本地激活”的权限,重新启动IIS之后再访问同一站点,则一切正常。 9:重启IIS中的特定应用程序池命令和自动重启的方法 在操作系统是Windows server 2003 SP1+的情况下,可以用以下命令部分重启IIS应用程序池: cscript.exe c:\windows\system32\iisapp.vbs /a "DefaultAppPool" 其中/a 代表alternatively,"DefaultAppPool"代表应用程序池的实例名。如果要设置自动重启这个应用程序池,可以尝试放在批处理中,用计划任务调用此批处理即可。很多人觉得计划任务不安全,都要禁掉,事实上,计划任务的不安全是建立在其它方面不安全的前提上的,如果由于其它方面的不安全,被放入执行程序,计划任务执行,这和计划任务没有直接关系。当然,关掉,是会减少一些安全隐患,这是不错。 原文地址:http://www.cnblogs.com/060218/archive/2009/11/05/1596428.html 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/11/05/1596556.html,如需转载请自行联系原作者
现在程序猿标配GIT作为代码管理,但是从SVN到GIT学习中,其中GIT的冲突是一个难点,常常会导致Push不上去,Pull不下来,很尴尬的地步,还不知道自己写的代码被覆盖没,废话不多说,直接上干货! 亮点 采用SourceTree插件和BeyondCompare 可视化解决冲突 方法 构造冲突 A 修改了conflict.file 中第1行内容并且提交到git上 B 这个时候也修改了confilct.file中第一行内容准备提交,这个时候git就会提示 To git@192.168.x.xxx:xxx/server-aggregator.git ! [rejected] develop -> develop (fetch first) error: failed to push some refs to 'git@192.168.xx.xx:xxx/server-aggregator.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. 提示远程已经有更新了,本地版本太低,让我们先pull拉取最新的代码。 我们pull一下,这个时候由于本地有修改这个文件,就会在本地产生冲突文件 配置外部比较工具 下载Beyond Compare 打开SourceTree->工具->选项->比较->外部差异对比合并->选择BeyondCompare 解决冲突 在本地副本->右键->解决冲突->打开外部合并工具 和svn一样解决好冲突保存更改,退出即可 另外一种情况 拉取时出现如下提示: it -c diff.mnemonicprefix=false -c core.quotepath=false pull local-server-aggregator develop /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell:3: warning: Insecure world writable dir /usr in PATH, mode 040777 From 192.168.0.200:weitoo/server-aggregator * branch develop -> FETCH_HEAD Updating b0c5c94..40cef3b error: Your local changes to the following files would be overwritten by merge: server/conflict.file Please, commit your changes or stash them before you can merge. Aborting 提示需要暂存本地修改,才能拉取服务器上新的代码 点击贮存(英文版:Stash),随便起一个名字,里面存的都是距离上次服务器版本到本地修改之间的差异,千万别删掉了,合并成功无误了再删掉。 pull拉取服务器代码,这个时候,本地的代码变成了服务器上的代码。 点击贮藏->应用贮藏区 ,这个时候是把之前的修改合并到本地上,这个时候会提示冲突。 git -c diff.mnemonicprefix=false -c core.quotepath=false stash apply stash@{0} Auto-merging server/conflict.file CONFLICT (content): Merge conflict in server/conflict.file 可以在sourcetree里看到有感叹号,代表冲突文件,和上面解决冲突方法类似,但是稍微不同,最左边成了远程版本,中间为远程上一个版本,最后才是本地修改。 这个是和我们操作方式有关:我们是先暂存本地修改,先拉取远程代码,这个时候local 就成了远程代码,最后我们用暂存的合并进去,remote就成了本地修改 多余的.orig文件 这个是由于git自身造成的 它会解决冲突后 生成一个原来冲突的备份,我们可以去掉 git config --global mergetool.keepBackup false 分类: 项目管理,项目有关 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7212231.html,如需转载请自行联系原作者
在写页面时,想把a标签设置成空链接,方便后面数据的连接可以有几种方法。 1、 <a herf=""></a> 这种方法会默认打开本页面,重新刷新一次页面。 2、<a herf="#"></a> 这种方法会在地址栏的后面添加一个#号,然后回到页面顶部。 3、<a herf="###"></a> 这种方法可以再chrome和ie11中,不再跳回页面顶部,但是还是会修改地址栏。 4、使用javascript伪协议 <a href="javascript:void(0);"></a> <a href="javascript:void(0)"></a> <a href="javascript:;"></a> <a href="javascript:"></a> 上面使用javascript的伪协议的作用相同,会使a标签链接到一个js方法,但这个方法是void(0),则不会触发。 “在IE6下面,未加分号的方案被点击后,IE6会使得页面中的gif暂停,并且触发onbeforeunload事件,IE6认作这个页面有了重定向,并abort之后所有的请求。所以假如你在此之后替换了一个<img>的src,IE6完全不会完成这个新的请求。” 分类: JAVASCRIPT 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7233566.html/,如需转载请自行联系原作者
XML Viewer WebPart可以将数据源为xml的内容显示到页面中,本文简单说明 1.新建一个页面用于测试使用,在Site Action下的New Page: 2.在Insert tab插入WebPart,选择Content Rollup类别下的XML Viewer: 3.XML Viewer添加到页面后点击open the tool pane: 4.在属性面板中将我们准备好的XML和XSL文件内容输入进去: Xml文件内容如下: <?xml version="1.0" encoding="utf-8" ?> <?xml-stylesheet type="text/xsl" href="Employees.xsl"?> <Employees> <Employee> <EmployeeID>1</EmployeeID> <EmployeeDes type="choice">Cary,your post?</EmployeeDes> <posts> <post>SSE</post> <post>SA</post> <post>PM</post> </posts> </Employee> <Employee> <EmployeeID>2</EmployeeID> <EmployeeDes type="choice">James,your post?</EmployeeDes> <posts> <post>SSE</post> <post>SA</post> <post>PM</post> </posts> </Employee> </Employees> Xsl文件内容如下: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <body> <h1>Employee Post Form</h1> <ol> <xsl:for-each select="//Employee"> <li> <strong> <xsl:value-of select="EmployeeDes"/> </strong> <br/> <xsl:for-each select="posts/post"> <span style="font-style: italic;"> <input type="radio"> <xsl:attribute name="name"> <xsl:value-of select="http://www.cnblogs.com/EmployeeID"/> </xsl:attribute> <xsl:value-of select="."/> </input> </span> <br/> </xsl:for-each> </li> <br/> </xsl:for-each> </ol> </body> </html> </xsl:template> </xsl:stylesheet> 5.确定后显示如下: 本文转自Justin博客园博客,原文链接:http://www.cnblogs.com/carysun/archive/2010/12/31/moss2010-xmlwp.html,如需转载请自行联系原作者
1.基本使用 CQWP只有在publishing site的网站模板中才可以使用,我们准备好站点后就可以通过下面方式插入WebPart: 可以在Query中设置该WebPart中要显示的内容: 在Additional Filters中设置Filter: 该WebPart可以定制具体要显示数据源中的那些字段,如下图所示,我们在Description中添加Page Content后左右的WebPart中就会将该内容显示在Webpart中: 2.定制样式 使用SPD打开站点的顶级站点,在All Files中XSL Style Sheets中可以看到一些关于Webpart的样式文件,和CQWP相关的有ContentQueryMain.xsl,Header.xsl,ItemStyle.xsl。 如果要新增加样式,我们可以新增template来自定义样式,完成后就可以在CQWP的styles属性中看到。 <xsl:template name=”NewDefaultTest” match=”*” mode=”itemstyle”>to</xsl:template> 本文转自Justin博客园博客,原文链接:http://www.cnblogs.com/carysun/archive/2011/01/03/moss2010-CQWP.html,如需转载请自行联系原作者
本帖提供两种做法,可避免在 SQL Server 事务锁定时产生的不正常或长时间阻塞,让用户和程序也无限期等待,甚至引起 connection pooling 连接数超过容量。 所谓的「阻塞」,是指当一个数据库会话中的事务,正在锁定其他会话事务想要读取或修改的资源,造成这些会话发出的请求进入等待的状态。SQL Server 默认会让被阻塞的请求无限期地一直等待,直到原来的事务释放相关的锁,或直到它超时 (根据 SET LOCK_TIMEOUT,本文后续会提到)、服务器关闭、进程被杀死。一般的系统中,偶尔有短时间的阻塞是正常且合理的;但若设计不良的程序,就可能导致长时间的阻塞,这样就不必要地锁定了资源,而且阻塞了其他会话欲读取或更新的需求。遇到这种情况,可能就需要手工排除阻塞的状态,而本文接下来要介绍两种排除阻塞的做法。 日前公司 server-side 有组件,疑似因撰写时 exception-handling 做得不周全,导致罕见的特殊例外发生时,让 SQL Server 的事务未执行到 cmmmit 或 rollback,造成某些表或记录被「锁定 (lock)」。后来又有大量的 request,要透过代码访问这些被锁定的记录,结果造成了严重的长时间「阻塞」,最后有大量 process (进程) 在 SQL Server 呈现「等待中 (WAIT)」的状态。 由于 SQL Server 的「事务隔离级别」默认是 READ COMMITTED (事务期间别人无法读取),加上 SQL Server 的锁定造成阻塞时,默认是别的进程必须无限期等待 (LOCK_TIMEOUT = -1)。结果这些大量的客户端 request 无限期等待永远不会提交或回滚的事务,并一直占用着 connection pool 中的资源,最后造成 connection pooling 连接数目超载。 查了一些书,若我们要查询 SQL Server 目前会话中的 lock 超时时间,可用以下的命令: SELECT @@LOCK_TIMEOUT 执行结果默认为 -1,意即欲访问的对象或记录被锁定时,会无限期等待。若欲更改当前会话的此值,可用下列命令: SET LOCK_TIMEOUT 3000 后面的 3000,其单位为毫秒,亦即会先等待被锁定的对象 3 秒钟。若事务仍未释放锁,则会抛回如下代号为 1222 的错误信息,可供程序员编程时做相关的逾时处理: 消息 1222,级别 16,状态 51,第 3 行已超过了锁请求超时时段。 若将 LOCK_TIMEOUT 设置为 0,亦即当欲访问对象被锁定时,完全不等待就抛回代号 1222 的错误信息。此外,此一 SET LOCK_TIMEOUT 命令,影响范例只限当前会话 (进程),而非对某个表做永久的设置。 ------------------------------------------------------------------------------------------- 接下来我们在 SSMS 中,开两个会话 (查询窗口) 做测试,会话 A 创建会造成阻塞的事务进程,会话 B 去访问被锁定的记录。 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->--会话 ABEGIN TRAN;UPDATE Orders SET EmployeeID=7 WHERE OrderID=10248--rollback; --故意不提交或回滚 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->--会话 BSELECT * FROM Orders WHERE OrderID=10248 分别执行后,因为欲访问的记录是同一条,按照 SQL Server 「事务隔离级别」和「锁」的默认值,会话 B 将无法读取该条数据,而且会永远一直等下去 (若在现实项目里写出这种代码,就准备被客户和老板臭骂)。 ------------------------------------------------------------------------------------------- 若将会话 B 先加上 SET LOCK_TIMEOUT 3000 的设置,如下,则会话 B 会先等待 3 秒钟,才抛出代号 1222 的「锁请求已超时」错误信息: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->--会话 B SET LOCK_TIMEOUT 3000SELECT * FROM Orders WHERE OrderID=10248--SET LOCK_TIMEOUT -1 执行结果: 消息 1222,级别 16,状态 51,第 3 行已超过了锁请求超时时段。语句已终止。 ------------------------------------------------------------------------------------------- 另根据我之前写的文章「30 分钟快快乐乐学 SQL Performance Tuning」所述:http://www.cnblogs.com/WizardWu/archive/2008/10/27/1320055.html 撰写不当的 SQL 语句,会让数据库的索引无法使用,造成全表扫描或全聚集索引扫描。例如不当的:NOT、OR 算符使用,或是直接用 + 号做来串接两个字段当作 WHERE 条件,都可能造成索引失效,变成全表扫描,除了性能变差之外,此时若这句不良的 SQL 语句,是本帖前述会话 B 的语句,由于会造成全表扫描或聚集索引扫描,因此就一定会被会话 A 的事务阻塞 (因为扫描全表时,一定也会读到 OrderID=10248 这一条会话 A 正在锁定的记录)。 下方的 SQL 语句,由于 OrderID 字段有设索引,因此下图 1 的「执行计划」,会以算法中的「二分查找法」在索引中快速查找 OrderID=10250 的记录。 SELECT * FROM Orders WHERE OrderID=10250 SELECT * FROM Orders WHERE OrderID=10250 AND ShipCountry='Brazil' 图 1 有正确使用到索引的 SQL 语句,以垂直的方向使用索引。用 AND 算符时,只要有任一个字段有加上索引,就能受惠于索引的好处,并避免全表扫描 此时若我们将这句 SQL 语句,当作前述会话 B 的语句,由于它和会话 A 所 UPDATE 的 OrderID=10248 不是同一条记录,因此不会受会话 A 事务未回滚的影响,会话 B 能正常执行 SELECT 语句。 但若我们将会话 B 的 SQL 语句,改用如下的 OR 算符,由于 ShipCountry 字段没有加上索引,此时会造成聚集索引扫描 (和全表扫描一样,会对整个表做逐条记录的 scan)。如此一来,除了性能低落以外,还会因为在逐条扫描时,读到会话 A 中锁定的 OrderID=10248 那一条记录,造成阻塞,让会话 B 永远呈现「等待中」的状态。 SELECT * FROM Orders WHERE OrderID=10250 OR ShipCountry='Brazil' 图 2 未正确使用索引的 SQL 语句,以水平的方向使用索引。用 OR 算符时,必须「所有」用到的字段都有加上索引,才能有效使用索引、避免全表扫描 ------------------------------------------------------------------------------------------- 发生阻塞时,透过以下命令,可看出是哪个进程 session id,阻塞了哪几个进程 session id,且期间经过了多少「毫秒 (ms)」。如下图 3 里 session id = 53 阻塞了 session id = 52 的进程。另透过 SQL Server Profiler 工具,也能看到相同的内容。 SELECT blocking_session_id, wait_duration_ms, session_id FROM sys.dm_os_waiting_tasks 图 3 本帖前述会话 A 的 UPDATE 语句 (53),阻塞了会话 B 的 SELECT 语句 (52) 透过以下两个命令,我们还能看到整个数据库的锁定和阻塞详细信息: SELECT * FROM sys.dm_tran_locks EXEC sp_lock 图 4 session id = 52 的 process 因阻塞而一直处于等待中 (WAIT) 另透过 KILL 命令,可直接杀掉造成阻塞的 process,如下: KILL 53 ------------------------------------------------------------------------------------------- 欲解决无限期等待的问题,除了前述的 SET LOCK_TIMEOUT 命令外,还有更省事的做法,如下,在会话 B 的 SQL 语句中,在表名称后面加上 WITH (NOLOCK) 关键字,表示要求 SQL Server,不必去考虑这个表的锁定状态为何,因此也可减少「死锁 (dead lock)」发生的机率。但 WITH (NOLOCK) 不适用 INSERT、UPDATE、DELETE。 SELECT * FROM Orders WITH (NOLOCK) WHERE OrderID=10248 类似的功能,也可如下,在 SQL 语句前,先设置「事务隔离级别」为可「脏读 (dirty read)」。 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDSELECT * FROM Orders WHERE OrderID=10248 两种做法的效果类似,让会话 B 即使读到被锁阻塞的记录,也永远不必等待,但可能读到别人未提交的数据。虽然说这种做法让会话 B 不用请求共享锁,亦即永远不会和其他事务发生冲突,但应考虑项目开发实际的需求,若会话 B 要查询的是原物料的库存量,或银行系统的关键数据,就不适合用这种做法,而应改用第一种做法的 SET LOCK_TIMEOUT 命令,明确让数据库抛回等候逾时的错误代号 1222,再自己写代码做处理。 ------------------------------------------------------------------------------------------- 归根究柢,我们在编程时,就应该避免写出会造成长时间阻塞的 SQL 语句,亦即应最小化锁定争用的可能性,以下为一些建议: 尽可能让事务轻薄短小、让锁定的时间尽量短,例如把不必要的命令移出事务外,或把一个大量更新的事务,切成多个更新较少的事务,以改善并发性。 将组成事务的 SQL 语句,摆到一个「批 (batch) 处理」,以避免不必要的延迟。这些延迟常由 BEGIN TRAN ... COMMIT TRAN 命令之间的网络 I/O 所引起。 考虑将事务的 SQL 语句写在一个存储过程内。一般来说,存储过程的执行速度会比批处理的 SQL 语句快,且存储过程可降低网络的流量和 I/O,让事务可更快完成。 尽可能频繁地认可 Cursor 中的更新,因为 Cursor 的处理速度较慢,会让锁定的时间较长。 若无必要,使用较宽松的事务隔离级别,如前述的 WITH (NOLOCK) 和 READ UNCOMMITTED。而非为了项目开发方便,全部使用默认的 READ COMMITTED 级别。 避免在事务执行期间,还要等待用户的反馈或交互,这样可能会造成无限期的持有锁定,如同本帖一开始提到的状况,最后造成大量的阻塞和数据库 connection 被占用。 避免事务 BEGIN TRAN 后查询的数据,可能在事务开始之前先被引用。 避免在查询时 JOIN 过多的表 (此指非必要的 JOIN),否则除了性能较差外,也很容易读到正被锁定或阻塞中的表和字段。 应注意在一个没有索引的表上,过量的「行锁」,或一些锁定使用了过多的内存和系统资源时,SQL Server 为了有效地管理这些锁定,会尝试将锁定扩展为整个表的「表锁」,此时会很容易造成其他 process 在访问时的阻塞和等待。 ------------------------------------------------------------------------------------------- 本文转自温景良(Jason)博客园博客,原文链接http://www.cnblogs.com/wenjl520/archive/2011/03/20/1989704.html,如需转载请自行联系原作者
现在大家都比较关心的问题就是在多用户高并发的情况下,如何开发系统,这对我们程序员来说,确实是值得研究,最近找工作面试时也经常被问到,其实我早有去关心和了解这类问题,但一直没有总结一下,导致面试时无法很完整全面的回答,所以今天我专门总结概况了一下关于SQL SERVER高并发解决方案,希望能帮助大家,若有不对之外,还请及时告之,谢谢! SQL SERVER高并发解决方案主要是从以下几个方面: 1.SQL语句优化: A.尽可能的精确查询条件及查询字段,缩小查询范围(包括使用分页查询); B.查询条件中尽可能少用:like,(not)in,(not)is null,order by,distinct,count(*),!=,<>; C.不要对查询的字段进行函数运算, 如:aa. substring('aa123',1,2)='aa',而应该是:'aa123' like 'aa%'; ---应用到了索引 bb. 'aa'+'123'='aa123',而应该是:'aa'=left('aa123',2) D.判断数据存在,不要使用TOP 1,而应该是:EXITS E.对于复杂SQL查询,可直接使用SQL存储过程或建立视图(视图不可太复杂); F.尽可能的少用游标,触发器; 2.表设计优化: A.纵向分割表设计,将表按照某种原则(可按照字段读写频率来设计)设计成相对应的几个表,之间采用主(外)键关联查询; B.横向分割表设计,将表中的数据按照使用价值(比如:只用只用到近3个月的有效数据)来进行数据转移备份,以减少表的数据量; C.表数据物理存放分区设计,将表中的数据按照某种规则建立物理表分区来存储,以降低硬盘的IO负担; D.建立适当的索引(聚集索引与非聚集索引); 3.事务设置优化: 事务隔离级别有:(隔离级别作用于事务中,而锁作用于每条SQL语句上) 隔离级别 脏读 不可重复读取 幻像 说明 产生或等同对应的锁 未提交读(read uncommitted) 是 是 是 如果其他事务更新,不管是否提交,立即执行 NOLOCK 提交读(read committed默认) 否 是 是 读取提交过的数据。如果其他事务更新没提交,则等待 HOLDLOCK 可重复读(repeatable read) 否 否 是 查询期间,不允许其他事务update HOLDLOCK 可串行读(serializable) 否 否 否 查询期间,不允许其他事务insert或delet HOLDLOCK A.事务隔离原则:共享读,排它写,即表示:在执行查询时,若对数据一致性要求很高时,可采用可重复读(repeatable read)隔离级别,若没有严格要求,则可建议使用未提交读(read uncommitted)隔离级别; 4.服务器硬件优化: A.服务器内存,硬盘等核心硬件性能当然越强越好; B.购买多台服务器并建立集群,以实现利用多个计算机进行并行计算从而获得很高的计算速度,也可以用多个计算机做备份,从而使得任何一个机器坏了整个系统还是能正常运行; C.在多台服务器建立DB镜像同步,并实现读写分离,即:除了指定的一台或几台服务器具有允许更新以外,其余的服务器均只作为数据镜像同步,不能更新,仅供查询; 分类: SQL 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6927543.html,如需转载请自行联系原作者
arcgis api for flex 开发入门(七)Geometry service 的使用 Geometry service 顾名思义,就是提供针对几何层级的服务,比如说Project, Simplify , Buffer,Areas And Lengths , Lengths 等 详细信息可以到下面的地址去看一下http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Geometry/Geo metryServer 这个网址发布了上面5个Geometry service。 下面我们就来讲解如何在你的flex 地图中使用Geometry service,我们拿最常用 buffer来做例子。 首先 定义一个GeometryService <esri:GeometryService id="myGeometryService" url="http://sampleserver2.arcgisonline.com/ArcGIS/rest/services/Geometr y/GeometryServer"/> id 唯一标识了这个GeometryService,url指定了GeometryService的地址 和identify工具类似,要进行buffer操作我们先创建一个需要做buffer的几何, 然后定义一个BufferParameters ,然后执行buffer操作,最后将buffer的结果绘 制到GraphicsLayer上。 下面我们介绍一下BufferParameters参数的意义distances为buffer半径, features 为需要做buffer的要素集合,unit为单位bufferSpatialReference为 buffer操作时的空间参照系。 设置完参数后,要添加buffer完成时间的EventListener,当buffer完成后我们就 可以处理这个buffer的结果了,如下面AS3脚本 var bufferParameters : BufferParameters = new BufferParameters(); bufferParameters.features = [point]; bufferParameters.distances = [3000]; bufferParameters.unit = BufferParameters.UNIT_METER; bufferParameters.bufferSpatialReference = new SpatialReference( 02113); myGeometryService.addEventListener (GeometryServiceEvent.BUFFER_COMPLETE, bufferCompleteHandler); myGeometryService.buffer( bufferParameters ); 关键的一点,bufferParameters.features参数设置的features 一定要有空间参 考系,不然buffer不成功。 其余的绘制操作,将结果绘制到GraphicsLayer上的操作和上一讲基本相同,就不 再讲了。 完整代码: Code Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><?xml version="1.0" encoding="utf-8"?><mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:esri="http://www.esri.com/2008/ags" layout="absolute" > <mx:Script> <![CDATA[ import com.esri.ags.Graphic; import com.esri.ags.events.DrawEvent; import com.esri.ags.events.IdentifyEvent; import com.esri.ags.geometry.Geometry; import com.esri.ags.symbol.Symbol; import com.esri.ags.toolbars.Draw; import com.esri.ags.SpatialReference; import com.esri.ags.events.GeometryServiceEvent; import com.esri.ags.tasks.BufferParameters; private function drawEndHandler(eventrawEvent):void { var geometry : Geometry = event.geometry; var myMapPoint:Graphic = new Graphic(); myMapPoint.geometry = geometry; geometry.spatialReference = new SpatialReference(4016); var bufferParameters : BufferParameters = new BufferParameters(); bufferParameters.features = [myMapPoint]; bufferParameters.distances = [300000]; bufferParameters.unit = BufferParameters.UNIT_METER; bufferParameters.bufferSpatialReference = new SpatialReference(102113); myGeometryService.addEventListener (GeometryServiceEvent.BUFFER_COMPLETE, bufferCompleteHandler); myGeometryService.buffer( bufferParameters ); } function bufferCompleteHandler( event : GeometryServiceEvent ) : void { myGeometryService.removeEventListener (GeometryServiceEvent.BUFFER_COMPLETE, bufferCompleteHandler); for each ( var graphic : Graphic in event.graphics ) { graphic.symbol = sfs; myGraphicsLayer.add( graphic ); } } ]]> </mx:Script> <esri:SimpleFillSymbol id="sfs" color="0xFF0000"> <esri:SimpleLineSymbol color="0x000000"/> </esri:SimpleFillSymbol> <!-- Draw ToolBar --> <esriraw id="drawToolbar" map="{map}" graphicsLayer="{myGraphicsLayer}" drawEnd="drawEndHandler(event)"> </esriraw> <esri:GeometryService id="myGeometryService" url="http://sampleserver2.arcgisonline.com/ArcGIS/rest/services/Geometr y/GeometryServer"/> <mxanel width="100%" height="100%"> <mx:Button label="buffer" click="drawToolbar.activate (Draw.MAPPOINT)"/> <esri:Map id="map" width="100%" height="100%"> <esri:ArcGISDynamicMapServiceLayer url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Special ty/ESRI_StatesCitiesRivers_USA/MapServer" /> <esri:GraphicsLayer id="myGraphicsLayer"/> </esri:Map></mxanel></mx:Application> 原文地址:http://bbs.esrichina-bj.cn/ESRI/viewthread.php?tid=35667&extra=page%3D4%26amp%3Borderby%3Ddateline 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2009/06/02/1494573.html,如需转载请自行联系原作者
背景 之前写过一篇关于同步饿了么订单的文章《订餐系统之同步饿了么商家订单》,有不少人加我咨询,感觉有这方面需求的人还是满多的,毕竟现在2家几乎瓜分了市场,再做平台已然机会渺茫了,但是商户毕竟需要服务,订单还得配送出去。然后饿了 么,美团外卖都提供了面向供应商的api的权限的申请,这对我们做配送系统的说,真是一大利好。以前都是让商户手动录入其他平台的订单,费力还容易出错。还得在多个app之间来回切换,商户也是抱怨满天。有了这些接口,商户可以选择自动接单, 自动同步订单,再也不用几个app来回切换了。饿了么同步订单,在上面的文章中已经介绍了,虽然接口现在变成2.0了。有了一些变化,总的来说还是 万变不离其宗。本篇就来详细接受下同步美团订单的相关步奏。写得不对地方,欢迎指正 :) 具体流程,下图中写得比较详细。 申请 去年过年,因娃太小,没能回家,闲在杭州,发现可以申请美团外卖相关接口,欣喜若狂,撸起袖子就动了起了,登录申请网站地址《美团点评 | 聚宝盆餐饮开放平台》,填写相关信息。记得,当时是2月5号,再2天就过年了,想着如果能年前审核好...,当然,后来是想多了,过了一个月都没有审核通过,也不能进行下一步。到时那个心呀,难过,没有还好,现在是看得见,不让用。经过漫长的煎熬,终于在一个不起眼的地方,发现一个邮箱,怀着死马当活马医的想法,发过去了,想不到,还真给回复了,说是过年漏掉了申请。好嘛,审核通过了总是好的,过程是复杂了些。 这里通过了,他们会加你QQ,确认相关信息,还会快递一份文件,签字盖章,按说明快递回去就OK了。 审核通过,开放平台里有相关信息了。关键的东西就是 developerId,与SignKey 。调用接口会用到 开入平台文档地址:《文档中心》,也许你看了文档,都不用看下面的:)。 回调接口设置 开始开发前,先要设置好回调,通知接口。美团外卖会根据在开放平台设置的通知地址,发送相关信息,比如新订单通知,商家确认订单通知,及绑定,解绑商家通知等。这里每个通知一个接口,这样程序蛮方便的,可以一个通知,一个url。不用去判断到底是什么通知。 在下图中设置 门店映射 所谓映射,就是把你系统的商家编号,与美团商家绑定起来,以后推送都是自己系统的商家编号推送过来。方便操作 对接第一步,还是绑定商家,并保存Token(后面的操作都会用到)。 绑定操作就是访问连接 https://open-erp.meituan.com/storemap?developerId=100019&businessId=2&ePoiId=8859&signKey=8bl1g62omy2m5ywp&ePoiName=湘北人家&netStore=1 输入商家账号,密码选择商家授权即可完成映射 具体参数可以参考文档, 其中 ePoiId 为自己系统的商家编号,像我就直接用自动编号,这个方便记录,后台绑定也方便,订单通知过来也方便。比如,我们最后的效果就是在商家详情中增加相关设置。 在回调接口设置中,设置了url后,会收到通知,处理通知的代码如下。 Response.Clear(); Response.Write("{\"data\":\"success\"}"); Hangjing.AppLog.AppLog.Info("美团门店与ERP绑定接收token回调URL"); System.IO.Stream stream = Request.InputStream;//这是你获得的流 if (stream != null && stream.Length > 10) { Hangjing.AppLog.AppLog.Info("stream.Length :" + stream.Length); string jsondata = ""; using (StreamReader reader = new StreamReader(stream)) { jsondata = reader.ReadToEnd(); ; } //保存 appAuthToken Hangjing.AppLog.AppLog.Info("回调URL信息:" + jsondata); } Response.Clear(); Response.Write("{\"data\":\"success\"}"); Hangjing.AppLog.AppLog.Info("美团门店与ERP绑定接收token回调URL"); System.IO.Stream stream = Request.InputStream;//这是你获得的流 if (stream != null && stream.Length > 10) { Hangjing.AppLog.AppLog.Info("stream.Length :" + stream.Length); string jsondata = ""; using (StreamReader reader = new StreamReader(stream)) { jsondata = reader.ReadToEnd(); ; } //保存 appAuthToken Hangjing.AppLog.AppLog.Info("回调URL信息:" + jsondata); } 既然有映射商家,就是有解除绑定的操作,同样,在回调接口设置中,设置了url后,会收到通知,处理通知的代码如下。 这里记录,主要是为了方便后台操作,知道当前商家是否绑定。 Response.Clear(); Response.Write("{\"data\":\"success\"}"); Hangjing.AppLog.AppLog.Info("美团门店与ERP解除绑定token回调URL"); System.IO.Stream stream = Request.InputStream;//这是你获得的流 if (stream != null && stream.Length > 10) { Hangjing.AppLog.AppLog.Info("stream.Length :" + stream.Length); string jsondata = ""; using (StreamReader reader = new StreamReader(stream)) { jsondata = reader.ReadToEnd(); ; } Hangjing.AppLog.AppLog.Info("解除绑定回调URL信息:" + jsondata); } Response.Clear(); Response.Write("{\"data\":\"success\"}"); Hangjing.AppLog.AppLog.Info("美团门店与ERP解除绑定token回调URL"); System.IO.Stream stream = Request.InputStream;//这是你获得的流 if (stream != null && stream.Length > 10) { Hangjing.AppLog.AppLog.Info("stream.Length :" + stream.Length); string jsondata = ""; using (StreamReader reader = new StreamReader(stream)) { jsondata = reader.ReadToEnd(); ; } Hangjing.AppLog.AppLog.Info("解除绑定回调URL信息:" + jsondata); } 新订单推送 要推送订单,首先得要设置接收通知的url 要推送订单,其次得有商家,所以美团为我们设置了测试商家。还可以添加多个。要特别注意的就是:测试门店为一个坐标位于南极洲、配送范围为西藏昌都的线上测试门店,开发者可使用美团外卖APP定位到西藏昌都气象局,根据测试门店名称搜索到测试门店进行下单。 一定要App定位在西藏昌都气象局方可下订单。 因为推送的数据格式是json,所以我的做法是:先创建好订单对应的体,收到通知后,转成实体,再做逻辑处理。接收通知,所上面的类似。 Response.Clear(); string order = Server.UrlDecode(Request["order"]); Hangjing.AppLog.AppLog.Info("美团订单来了.美团订单内容:" + order); apiResultInfo rs = new apiResultInfo(); System.IO.Stream stream = Request.InputStream;//这是你获得的流 if (stream != null && stream.Length > 10) { Hangjing.AppLog.AppLog.Info("stream.Length :" + stream.Length); string jsondata = ""; using (StreamReader reader = new StreamReader(stream)) { jsondata = reader.ReadToEnd(); ; } Hangjing.AppLog.AppLog.Info("美团订单信息:" + jsondata); mtorderInfo model = JsonConvert.DeserializeObject<mtorderInfo>(order); if (model.ePoiId == "1015") { mthelper mt = new mthelper(Context); apiResultInfo retuls = mt.confirmOrder(model.orderId, Convert.ToInt32(model.ePoiId)); } } else { rs.state = 0; rs.msg = "参数错误"; } Response.Write("{\"data\":\"OK\"}"); Response.End(); Response.Clear(); string order = Server.UrlDecode(Request["order"]); Hangjing.AppLog.AppLog.Info("美团订单来了.美团订单内容:" + order); apiResultInfo rs = new apiResultInfo(); System.IO.Stream stream = Request.InputStream;//这是你获得的流 if (stream != null && stream.Length > 10) { Hangjing.AppLog.AppLog.Info("stream.Length :" + stream.Length); string jsondata = ""; using (StreamReader reader = new StreamReader(stream)) { jsondata = reader.ReadToEnd(); ; } Hangjing.AppLog.AppLog.Info("美团订单信息:" + jsondata); mtorderInfo model = JsonConvert.DeserializeObject<mtorderInfo>(order); if (model.ePoiId == "1015") { mthelper mt = new mthelper(Context); apiResultInfo retuls = mt.confirmOrder(model.orderId, Convert.ToInt32(model.ePoiId)); } } else { rs.state = 0; rs.msg = "参数错误"; } Response.Write("{\"data\":\"OK\"}"); Response.End(); 确认订单 我们收到新订单通知时,可以调用api直接确认订单,也就是商户接单。到这里,我们才正式去调用api,前面都是美团来调用我们,要调用api,首先要做的就是实现签名。这是我对接这么多系统,签名算法最简单的。直接上代码。 签名:首先将GET、POST参数除去sign本身以及值为空的参数以及=号和&号,按参数自然排序,(例如a=&c=3&b=1,变为b1c3)然后按参数1值1参数2值2...参数n值n的方式拼接成新字符串,再跟字符串前面加上signKey,做sha1散列,最后将得出字符串转成小写即为sign。 签名代码 /// <summary> /// sha1签名 /// </summary> /// <returns></returns> public string createSHA1Sign() { StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = HttpUtility.UrlEncode((string)parameters[k], Encoding.UTF8); if (null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0) { sb.Append(k+ v); } } string signstep2 = Constant.mt_SignKey + sb.ToString(); string sig = Utils.SHA1_Hash(signstep2).ToLower(); AppLog.AppLog.Info("美团签名=" + "signstep1=" + sb.ToString() + "\r\nsignstep2=" + signstep2 + "\r\n => sign:" + sig); return sig; } /// <summary> /// sha1签名 /// </summary> /// <returns></returns> public string createSHA1Sign() { StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = HttpUtility.UrlEncode((string)parameters[k], Encoding.UTF8); if (null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0) { sb.Append(k+ v); } } string signstep2 = Constant.mt_SignKey + sb.ToString(); string sig = Utils.SHA1_Hash(signstep2).ToLower(); AppLog.AppLog.Info("美团签名=" + "signstep1=" + sb.ToString() + "\r\nsignstep2=" + signstep2 + "\r\n => sign:" + sig); return sig; } 录入所有参数,得到签名,初始化post参数,调用指定api,即可完成确认。大概代码如下,代码都比较文明,大家一看变懂了。 public apiResultInfo confirmOrder(string orderId, int shopid) { apiResultInfo rs = new apiResultInfo(); meituanbindlogInfo record = new meituanbindlog().GetList(1, 1, "ePoiId=" + shopid + " and mtype=1", "mid", 1).FirstOrDefault(); if (record == null) { OperationLog.Warn("商家:" + shopid + "未找到绑定token"); rs.msg = "商家:" + shopid + "未找到绑定token"; return rs; } parameters = new Hashtable(); parameters.Add("charset", "UTF-8"); parameters.Add("version", "1"); parameters.Add("timestamp", Utils.getTimestamp()); parameters.Add("appAuthToken", record.appAuthToken); parameters.Add("orderId", orderId.Replace("m", "")); createSHA1Sign(); HttpItem objHttpItem = new HttpItem() { Encoding = "utf-8", Method = "POST" }; string url = "http://api.open.cater.meituan.com/waimai/order/confirm"; string returnmsg = queryData(objHttpItem, url); mtresult result = Newtonsoft.Json.JsonConvert.DeserializeObject<mtresult>(returnmsg); if (result.data != null && result.data.ToLower() == "ok") { rs.state = 1; } else { rs.msg = "确认订单失败"; } return rs; } public string queryData(HttpItem objHttpItem, string url) { string sig = createSHA1Sign(); parameters.Add("sign", sig); serverurl = url; ArrayList akeys = new ArrayList(parameters.Keys); StringBuilder sb = new StringBuilder(); foreach (string k in akeys) { string v = context.Server.UrlEncode((string)parameters[k]); v = HttpUtility.UrlEncode(v, Encoding.UTF8); if (null != v && "".CompareTo(v) != 0) { if (sb.Length == 0) { sb.Append(k + "=" + v); } else { sb.Append("&" + k + "=" + v); } } } if (objHttpItem.Method.ToLower() == "get") { serverurl += "?" + sb.ToString(); } else { objHttpItem.Postdata = sb.ToString(); } HttpHelper objhttp = new HttpHelper(); objHttpItem.URL = serverurl; objhttp.isToLower = false; string returnmsg = objhttp.GetHtml(objHttpItem); Hangjing.Common.HJlog.toLog("returnmsg=" + returnmsg + "\r\n url=" + serverurl); // HJlog.toLog(this.getDebugInfo()); return returnmsg; } public apiResultInfo confirmOrder(string orderId, int shopid) { apiResultInfo rs = new apiResultInfo(); meituanbindlogInfo record = new meituanbindlog().GetList(1, 1, "ePoiId=" + shopid + " and mtype=1", "mid", 1).FirstOrDefault(); if (record == null) { OperationLog.Warn("商家:" + shopid + "未找到绑定token"); rs.msg = "商家:" + shopid + "未找到绑定token"; return rs; } parameters = new Hashtable(); parameters.Add("charset", "UTF-8"); parameters.Add("version", "1"); parameters.Add("timestamp", Utils.getTimestamp()); parameters.Add("appAuthToken", record.appAuthToken); parameters.Add("orderId", orderId.Replace("m", "")); createSHA1Sign(); HttpItem objHttpItem = new HttpItem() { Encoding = "utf-8", Method = "POST" }; string url = "http://api.open.cater.meituan.com/waimai/order/confirm"; string returnmsg = queryData(objHttpItem, url); mtresult result = Newtonsoft.Json.JsonConvert.DeserializeObject<mtresult>(returnmsg); if (result.data != null && result.data.ToLower() == "ok") { rs.state = 1; } else { rs.msg = "确认订单失败"; } return rs; } public string queryData(HttpItem objHttpItem, string url) { string sig = createSHA1Sign(); parameters.Add("sign", sig); serverurl = url; ArrayList akeys = new ArrayList(parameters.Keys); StringBuilder sb = new StringBuilder(); foreach (string k in akeys) { string v = context.Server.UrlEncode((string)parameters[k]); v = HttpUtility.UrlEncode(v, Encoding.UTF8); if (null != v && "".CompareTo(v) != 0) { if (sb.Length == 0) { sb.Append(k + "=" + v); } else { sb.Append("&" + k + "=" + v); } } } if (objHttpItem.Method.ToLower() == "get") { serverurl += "?" + sb.ToString(); } else { objHttpItem.Postdata = sb.ToString(); } HttpHelper objhttp = new HttpHelper(); objHttpItem.URL = serverurl; objhttp.isToLower = false; string returnmsg = objhttp.GetHtml(objHttpItem); Hangjing.Common.HJlog.toLog("returnmsg=" + returnmsg + "\r\n url=" + serverurl); // HJlog.toLog(this.getDebugInfo()); return returnmsg; } 确认订单后,我们设置的订单确认通知接口,也有收到相应的通知,具体代码与新订单通知是一样的,只是你可以根据自己业务处理,比如,商家确认订单后,订单直接进入系统,自动调度,或者人工派单。 设置商家营业状态 这个接口也是经常用到的,因为测试账号,不能登录美团外卖商户后台,如果商家有一个订单没接,商家就会自动休息,影响我们测试。所以实现这个接口后,可方便修改商家状态。 这个接口与确认订单接口都是类似的,只是参数不同,url不同而已。由于调用api,我们都是封装在方法:queryData了,我们只用设置好参数即可。代码如下: public apiResultInfo shopOnline(int shopid) { apiResultInfo rs = new apiResultInfo(); meituanbindlogInfo record = new meituanbindlog().GetList(1, 1, "ePoiId=" + shopid + " and mtype=1", "mid", 1).FirstOrDefault(); if (record == null) { OperationLog.Warn("商家:" + shopid + "未找到绑定token"); rs.msg = "商家:" + shopid + "未找到绑定token"; return rs; } parameters.Add("appAuthToken", record.appAuthToken); createSHA1Sign(); HttpItem objHttpItem = new HttpItem() { Encoding = "utf-8", Method = "POST" }; string url = "http://api.open.cater.meituan.com/waimai/poi/open"; string returnmsg = queryData(objHttpItem, url); mtresult result = Newtonsoft.Json.JsonConvert.DeserializeObject<mtresult>(returnmsg); if (result.data != null && result.data.ToLower() == "ok") { rs.state = 1; } else { rs.msg = "商家上线失败"; } return rs; } 设置商家上线 public apiResultInfo shopOnline(int shopid) { apiResultInfo rs = new apiResultInfo(); meituanbindlogInfo record = new meituanbindlog().GetList(1, 1, "ePoiId=" + shopid + " and mtype=1", "mid", 1).FirstOrDefault(); if (record == null) { OperationLog.Warn("商家:" + shopid + "未找到绑定token"); rs.msg = "商家:" + shopid + "未找到绑定token"; return rs; } parameters.Add("appAuthToken", record.appAuthToken); createSHA1Sign(); HttpItem objHttpItem = new HttpItem() { Encoding = "utf-8", Method = "POST" }; string url = "http://api.open.cater.meituan.com/waimai/poi/open"; string returnmsg = queryData(objHttpItem, url); mtresult result = Newtonsoft.Json.JsonConvert.DeserializeObject<mtresult>(returnmsg); if (result.data != null && result.data.ToLower() == "ok") { rs.state = 1; } else { rs.msg = "商家上线失败"; } return rs; } 上线 其他还有很多接口可以按需对接,比如商口数据,评论数据。目前我们用不上,暂未对接。最后一步就上线操作了。 未上线之前,最多能接5个商家,上线后就没有限制了。 目前上线要先写一个《上线申请》,然后在开放平台,里点击上线,会有美团人员联系你,确认相关信息就OK了。 最后效果图如下: 结语 对接美团外卖,除了提交审核时间不好,导致审核时间长了些外,其他还是蛮顺利的,他们也蛮配合的,根据接口文档几乎可以完整大部分操作。 就代码上而言,确定没有太多可以说的,对接过程还是有些曲折的。希望这个文章能给那个正在对接的,想对接的人提供一些帮助,如果真有帮助了,有个赞最好了。 转: http://www.cnblogs.com/jijunjian/p/6875909.html 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7500173.html,如需转载请自行联系原作者
1、swift中使用class创建一个类。一个类的声明则是在类里作为常量或变量声明的,除了是在类的上下文中。在方法和函数中也是这么写的。 2、swift中使用init(...)作为初始化构造函数 3、swift中使用构造函数初始化成员变量时,格式为 : self.name = name.构造器的声明跟函数一样,除了会创建类的实例。每一个属性都需要赋值,无论在声明里还是在构造器里。 4、swift中使用deinit来创建一个析构函数,由系统自动调用来撤销对象,进行内存的清理工作 5、swift中子类继承父类时,一冒号:隔开。在继承标准根类时无需声明,所以你可以忽略超类 6、swift中子类重写父类的方法时,必须使用关键词override重载超类中的实现,如果没有这个关键词,编译器会报错 7、swift中可以设置get和set方法,例如如下所示: var perimeter: Double { //getter方法 get{ return slideLength } //setter方法 set{ slideLength = newValue/2 } } 具体举例如下: 定义一个没有构造函数的类Shape //声明一个没有构造函数的类 class Shape{ //成员变量,边数 var numberOfSides = 0 //成员方法 func simpleDescripton()->String{ return "A shape with \(numberOfSides) sides." } } //创建对象 var shape = Shape() //{numberOfSides 0} //设置成员变量 shape.numberOfSides = 4 //调用成员方法 shape.simpleDescripton() //"A shape with 4 sides" 定义一个子类NameShape继承上面的父类Shape,子类带一个init构造函数和一个deinit析构函数 //声明带构造函数的类,继承父类Shape class NameShape : Shape{ //形状的名字 var name:String //初始化方法 init(name:String){ self.name = name } //虚构方法,不可以显示调用,系统会在程序执行结束后自动调用,清理内存 deinit{ println("deinit") } //成员方法,重写父类的方法必须要加上关键字 override override func simpleDescripton()->String{ return "A shape with name:\(name) have \(numberOfSides) sides." } } //创建对象,参数名不可以省略 var nameshape:NameShape = NameShape(name:"rect") //{{numberOfSides 0} name"rect"} //设置成员变量 nameshape.numberOfSides = 4 //{{numberOfSides 4} name "rect"} //调用成员方法 nameshape.simpleDescripton() //"A shape with name:rect have 4 sides" 定义一个正方形类Square继承上面的父类NameShape,其实它也继承了根父类Shape,Square继承它父类所有公有的属性和方法,在初始化时,子类可以借助父类的初始化方法给它们共有的属性赋初值 //继承父类NameShape class Square: NameShape { //边长 var slideLength:Double = 0.0 //子类自己的初始化方法 init(slideLength:Double,name:String) { self.slideLength = slideLength //调用父类的初始化方法 super.init(name: name) //直接用从父类继承的边数属性 numberOfSides = 4 } //设置对象的set和get方法 var perimeter: Double { //getter方法 get{ return slideLength } //setter方法 set{ slideLength = newValue/2 } } //定义求面积的方法 func area() -> Double{ return slideLength*slideLength } override func simpleDescripton() -> String { return "A shape with name:\(name) have \(numberOfSides) sides.its area is \(area())" } } //创建对象时的参数名不可以省略 var square:Square = Square(slideLength:2,name:"正方形") square.perimeter = 6 square.simpleDescripton() //"A shape with name:正方形 have 4 sides .its area is 9.0" square.slideLength //3 可以给参数的参数名再设置一个名字,函数内部使用参数的原始名,调用时使用设置参数名的名称,即外部名,前一章函数部分已经介绍过 class Counter{ var count:Int = 0 //给第二个参数times再设置一个名称numberOfTimes,不过函数内部使用的还是times func incrementBy(amount: Int, numberOfTimes times: Int){ count += amount * times } } var counter = Counter() //创建一个对象//调用函数时,用的第二个参数的名称为numberOfTimes counter.incrementBy(2, numberOfTimes: 7) //14 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4903057.html,如需转载请自行联系原作者
️️️swift中的Optional Type的?和!含义:其实就是一个装包和拆包的过程 optional的含义: Optional事实上是一个枚举类型,Optional包含None和Some两种类型,而nil就是Optional.None,非nil就是Optional.Some。如果Optional变量在声明时不初始化,swift会调用init()来初始化变量为nil,而用非nil的值来初始化变量时,会通过Some(T)把该原始值包装,所以在之后使用的时候我们需要通过解包取出原始值才能使用。 问号? a.声明时添加?,告诉编译器这个是Optional的,如果声明时没有手动初始化,就自动初始化为nil b.在对变量值操作前添加?,判断如果变量是nil,则不响应后面的方法。 叹号! a.声明时添加!,告诉编译器这个是Optional的,并且之后对该变量操作的时候,都隐式的在操作前添加! b.在对变量操作前添加!,表示默认为非nil,直接解包进行处理 使用?的场景: 可选?装包: 使用!的场景: 可选隐式解包: 必须使用!解包: 总结一下: ️️️swift和Objective-C混编使用 swift中使用OC: 在项目工程的Build Settings中搜索并设置Bridging-Header-------- 头文件桥接,xxx(项目名)-Bridging-Header.h中导入OC中需要的类头文件,即 #import “Header.h” OC中使用swift: 在项目工程的Build Settings中搜索并设置packaging中的Product Module Name的名称—————然后在OC类中导入swift头文件,即 #import xxx(Product Module Name)-Swift.h 混编的具体步骤演示如下: 前期工作: 第一步:创建一个swift工程文件Project,生成Project文件 第二步:在建好的swift项目中新建一个swift文件,设置类名为学生类Student,点击Next创建成功,生成Student.swift文件 第三步:再创建一个Objective-C文件,设置类名为课程类Course 第四步:点击Next,此时弹出一个弹出框,点击yes即可,生成Course.h和Course.m文件,还有xxx(工程名)-Bridging-Header.h文件,即 Project-Bridging-Header.h 好了,前期工作准备完毕,现在就是Objective-C和Swift的类混编使用了: 在Swift中使用Objective-C的类: 1.选中Project-Bridging-Header.h,进入它的文件中,然后导入Course.h头文件,即桥接双方的头文件 2.此时就可以在Student.swift中使用Course类了,如下所示 在Objective-C中使用Swift的类: 方法一: 1.进入工程的面板中找到Build Settings,搜索packaging,截图如下 2.可以看到Product Module Name这一项,既可以复制他后面的名字作为 $(PRODUCT_NAME:c99extidentifier)-swift.h头文件,当然也可以自己重新设置名称,不过不能以数字0开头,命名要规范。我设置一个新名字为"ProductName-swift.h",然后按Command+B快捷键编译一下,因为必须先编译生成Student.swift中的所有内容,然后才能在Course类中导入这个头文件 3.编译成功后,就可以在Course类中导入ProductName-swift.h了,即#import "ProductName-swift.h",可以看到如下我声明得到学生对象 此时,你可以按住Command键,然后点击#import “ProductName-Swift.h”可以进去看看编译的所有文件 方法二: 在创建工程时,可以直接在Objective-C的文件中导入固定的文件方式并编译一下即可,也即: import "ProjectName-Swift.h",这个ProjectName:开始创建的工程名 在此实例下,导入方式为:import "Project-Swift.h" 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4904379.html,如需转载请自行联系原作者
swift中使用protocol声明一个协议接口 swift中类、枚举和结构体都可以实现协议接口 swift中类中的方法都可以修改成员变量的值 swift中结构体中的方法默认是不能修改成员变量的,添加mutating关键字后就可以修改了 swift中使用extension来为现有的类型添加功能。你可以使用扩展来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型 swift中协议接口的委托代理模式和Objective-C基本上是一样的,都是主方设置一个代理人,然后将事情的处理委托给这个代理人去办,而主方自己只需要声明协议的规则即可。记住,代理人必须按照主方设定的规则去处理这些需要做的事情。不管代理人是谁,只要遵从协议就可以成为主方的委托者。 Swift 中的扩展可以: 1. 添加计算型属性和计算静态属性 2. 定义实例方法和类型方法 3. 提供新的构造器 4. 定义下标 5. 定义和使用新的嵌套类型 6. 使一个已有类型符合某个接口 具体举例如下: //声明一个ExampleProtocol协议,有一个成员变量和一个默认必须实现的方法 //声明一个协议 protocol ExampleProtocol{ //声明一个成员变量,并设置一个getter方法 var simpleDescription:String{get} mutating func adjust() } //定义一个SimpleClass类继承ExampleProtocol协议 //定义一个类继承协议 class SimpleClass: ExampleProtocol { var simpleDescription:String = "A very simple class" func adjust() { simpleDescription += " adjust" } } var simpleclass = SimpleClass() //创建对象 simpleclass.adjust() //实现下一中的必须实现的方法 simpleclass.simpleDescription //用get方法获取属性值 "A very simple class adjust" //定义一个SimpleStruct结构体继承ExampleProtocol协议 //定义一个结构体继承协议 struct SimpleStruct:ExampleProtocol { var simpleDescription:String = "A very simple struct" //必须用关键词mutating修饰,才能修改属性值 mutating func adjust() { simpleDescription += "struct" } } var simplestruct = SimpleStruct() //创建结构体成员变量 simplestruct.adjust() //实现下一中的必须实现的方法 simplestruct.simpleDescription //用get方法获取属性值 "A very simple structstruct --------------------------------------------------------------------------------------------------------- //声明一个Myprotocol协议,有一个成员变量和一个默认必须实现的方法,还有一个可选的方法 //定义一个有可选方法的协议 @objc protocol MyProtocol{ //声明一个成员变量,并设置一个getter和setter方法 var contentDescription:String {get set} func method() //可选的方法 optional func adjust() } //定义一个Boy类继承Myprotocol这个协议 class Boy:MyProtocol{ var contentDescription:String = "he is a boy" //实现可选的方法 func method() { contentDescription += ",good dood study" } //实现必须实现的方法 func adjust(){ contentDescription += ",okay" } } let boy = Boy() boy.contentDescription = "Tom" //set方法赋值 boy.method() //实现必须实现的方法 boy.adjust() //实现可选的方法 boy.contentDescription //get方法取值 "Tom ,good good study,okay" --------------------------------------------------------------------------------------------------------- //定义一个扩展继承ExampleProtocol协议 //定义一个类扩展 extension Int:ExampleProtocol{ var simpleDescription:String{ return "The number \(self)" } mutating func adjust(){ self += 24 } } 7.simpleDescription // 7 //自定义一个扩展 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4905128.html如需转载请自行联系原作者
对象数组 对象数组:每一个数组元素都是对象的数组,也就是说,若一个类有若干个对象,我们把这 一系列的对象用一个数组来存放。对应数组元素是对象,不仅具有的数据成员,而且还有函数 成员。 @定义一个一维数组的格式如: 类名 数组名[下标表达式]; @使用对象数组时只能访问单个数组元素,其一般格式如: 数组名[下标].成员名 举例:Complex com[2]={11,22};//定义类Complex的实参数1个的对象数组com,含有n个对象数组元素 举例:Complex com[2]={ Complex(11,22), Complex(33,44), }; //定义的实参个数为2个的对象数组com,含有n个对象数组元素 /* //例3.14 用只有一个参数的构造函数给对象数组赋值 #include<iostream> using namespace std; class exam{ public: exam(int n) //只有一个参数的构造函数 { x = n; } int getx() { return x; } private: int x; }; int main() { exam obj[3]={11,22,33}; //用只有一个参数的构造函数给对象数组进行赋值,三个对象 for(int i=0;i<=2;i++) cout<<"第"<<i+1<<"个对象是: "<<"obj["<<i<<"]"<<" = "<<obj[i].getx()<<endl; return 0; } 运行结果: 第1个对象是: obj[0] = 11 第2个对象是: obj[1] = 22 第3个对象是: obj[2] = 33 // 例3.15 用不带参数和带一个参数的构造函数给对象数组赋值 #include<iostream> using namespace std; class exam{ public: exam() //不带参数的构造函数 { x = 88; } exam(int n) //只有一个参数的构造函数 { x = n; } int getx() { return x; } private: int x; }; int main() { exam obj1[3]={11,22,33}; //三个对象均用只有一个参数的构造函数给对象数组进行赋值 exam obj2[3]={44}; //第一对象调用有一个参数的构造函数赋值,后两个对象调用无参的构造函数赋默认值 for(int i=0;i<=2;i++) cout<<"第"<<i+1<<"个对象是: "<<"obj1["<<i<<"]"<<" = "<<obj1[i].getx()<<endl; cout<<endl; for(int i=0;i<=2;i++) cout<<"第"<<i+1<<"个对象是: "<<"obj2["<<i<<"]"<<" = "<<obj2[i].getx()<<endl; return 0; } 运行结果: 第1个对象是: obj1[0] = 11 第2个对象是: obj1[1] = 22 第3个对象是: obj1[2] = 33 第1个对象是: obj2[0] = 44 第2个对象是: obj2[1] = 88 第3个对象是: obj2[2] = 88 */ //例3.16 用带有多个参数的构造函数给对象数组赋值。 #include<iostream> #include<cmath> using namespace std; class Complex{ public: Complex(double r=0.0,double i=0.0):real(r),imag(i) {}//定义带有默认参数的构造函数,用成员初始化列表对数据成员进行初始化 ~Complex() { cout<<"Destructor called."<<endl; } double abscomplex() { double t; t = real*real+imag*imag; return sqrt(t); } private: double real; double imag; }; int main() { Complex A[3]={ Complex(1.1,2.2), Complex(3.3,4.4), Complex(5.5,6.6), }; for(int i=0; i<=2; i++) cout<<"复数对象A["<<i<<"]的绝对值是: "<<A[i].abscomplex()<<endl; return 0; } 运行结果:复数对象A[0]的绝对值是: 2.45967 复数对象A[1]的绝对值是: 5.5 复数对象A[2]的绝对值是: 8.59127 Destructor called. Destructor called. Destructor called. 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: C++ 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4911838.html,如需转载请自行联系原作者
C# 关闭进程的时候总是捕捉到System.Threading.ThreadAbortException: 正在中止线程 这是由ThreadAbortException抛出的 可以写成下面的样子 try { } catch (ThreadAbortException ex) { //不作处理 } catch(Exception ex) { //处理 } 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7757725.html,如需转载请自行联系原作者
Python中的Decorators表面看起来很像C#的Attribute,其实不然,Python的Decorators和C#的Attribute完全是两个东西。Python的Decorators让我想到了设计模式中的装饰者模式(Decorator Pattern)。 Decorator Pattern Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionnality. Python中的通过Decorators对函数、方法或类进行装饰,从而达到增加对象的职责,或控制对象调用的作用。而C#的Attribute仅仅是起到元数据标识作用,最终通过反射获取这些特定信息。 先来个简单的示例,先定义一个Coffee类, Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->class Coffee(object): def get_cost(self): return 1.0 coffee = Coffee()print coffee.get_cost() # 1.0 这时,我想通过装饰者模式计算Milk的价格,通常这样实现: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->class Milk(Coffee): def __init__(self, coffee): self.coffee = coffee def get_cost(self): return self.coffee.get_cost() + 0.5 coffee = Coffee() coffee = Milk(coffee)print coffee.get_cost() # 1.5 上面是经典的装饰者模式的实现,Python中通过Decorators可以实现成这样: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->def milk_decorator(get_cost): def get_milk_cost(self): return get_cost(self) + 0.5 return get_milk_costclass Coffee(object): @milk_decorator def get_cost(self): return 1.0 coffee = Coffee()print coffee.get_cost() #1.5 假设一下,如果有更多的,比如:Whip, Sprinkles, Tee, 必须为每个装饰者都实现一个函数,将会出现函数爆炸,我们可以只实现一个通用的Decorator函数,通过在get_cost函数添加多个@Decorator,这很符合Decorator Pattern的思想。 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->def get_cost_decorator(additional_cost): def wrapper1(func): def wrapper2(instance): return func(instance) + additional_cost return wrapper2 return wrapper1class Coffee(object): @get_cost_decorator(0.5) @get_cost_decorator(0.7) @get_cost_decorator(0.2) def get_cost(self): return 1.0 coffee = Coffee()print coffee.get_cost() #2.4 上面的get_cost_decorator类看上去比较复杂,不要紧,一会再回头看这个函数。 Decorators基础 闲话不多说,先看下面的简单例子: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->def myDecorator(func): def newFunction(): print "inside newFunction" func() return newFunction @myDecoratordef aFunction(): print "inside aFunction()" aFunction() 最终输出: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->inside newFunction inside aFunction() 我们看到,myDecorator函数的参数其实是aFunction的函数地址,并且返回一个函数地址,返回的函数才是最终真正调用的地址。最终的调用,等价于: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->aFunction = myDecorator(aFunction) aFunction() 其中,myDecorator也可以使用class来实现,比如: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->class myDecorator(object): def __init__(self, func): self.func = func def __call__(self): print "inside myDecorator" self.func() @myDecoratordef aFunction(): print "inside aFunction()" 最终, Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->aFunction() 相对于 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->aFunction = myDecorator(aFunction) aFunction() # __call__ Decorators调用规律 上面的例子,我们可以很容易的得到这样一个规律: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->@Adef f (): … 最终等价于: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->f = A(f) 如果更复杂一些: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->@A @B @Cdef f (): … 则相对于: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->f = A(B(C(f))) 再看看有参数的例子, Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->@A(args)def f (): … 这时,f相当于: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->_deco = A(args) f = _deco(f) 因此,A的实现也会相对复杂一些: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->def A(args): def wrapper1(f): def wrapper2(): print “before call f()” f() return wrapper2 return wrapper1 有点绕吧,嗯,还算简单,我们回头看最开头那个例子, Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->@get_cost_decorator(0.5) @get_cost_decorator(0.7) @get_cost_decorator(0.2)def get_cost(self): return 1.0 相当于: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->get_cost = get_cost_decorator(0.5)(get_cost_decorator(0.7)(get_cost_decorator(0.2)(get_cost))) # 绕晕了~~ Decorators典型应用 – singleton class Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->def singleton(cls): instances = {} def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance @singletonclass MyClass: ... 参考文章: Decorators for Functions and Methods Introduction to Python Decorators Decorator pattern [Python学习]decorator的使用 - limodou Python 天天美味系列(总) Python 天天美味(30) - python数据结构与算法之快速排序 Python 天天美味(31) - python数据结构与算法之插入排序 Python 天天美味(32) - python数据结构与算法之堆排序 Python 天天美味(33) - 五分钟理解元类(Metaclasses)[转] Python 天天美味(34) - Decorators详解 本文转自CoderZh博客园博客,原文链接:http://www.cnblogs.com/coderzh/archive/2010/04/27/python-cookbook33-Decorators.html,如需转载请自行联系原作者
.由地名(省份、城市、街道等)得到其对应的百度地图坐标: http://api.map.baidu.com/geocoder/v2/?output=json&ak=你从百度申请到的Key&address=北京市 其对大陆主要城市的解析很好,但是有些大陆的小城市、香港、台湾的一些区域无法解析,我测试到的如下: 解析错误的城市:伊犁|新界|新竹|港岛|基隆|云林|博尔塔拉|桃园|苗栗|南投|克孜勒苏|台东| 伊犁可以使用新疆伊犁来解析,更好的办法是使用谷歌地图API,比如这样使用: http://maps.google.com/maps/api/geocode/json?sensor=false&address=地址 在谷歌地图中,港岛可以使用: Hong Kong Island 来解析, 云林可以使用: Yunlin County, Taiwan 来解析, 博尔塔拉可以使用博尔塔拉蒙古自治州来解析, 2.由坐标反解得到对应的地址: http://api.map.baidu.com/geocoder/v2/?output=json&ak=你从百度申请到的Key&location=纬度(Latitude),经度(Longitude) 注意,纬度和经度之间有个英文逗号。 具体参考:http://developer.baidu.com/map/webservice-geocoding.htm 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7552535.html,如需转载请自行联系原作者
本文中所有算法均搜集于网络,在此感谢各位大师的无私奉献。 主函数: 1 const int max = 1000; // 最大取值范围 2 //const int count = 5000; // 小数据量 3 const int count = 100000000; // 数据量 4 const int resultLen = 4; // 返回长度 5 6 static void Main(string[] args) 7 { 8 var random = new Random(2010); // 固定随机种子,确保大家测试数据一致 9 var data = new int[count]; 10 #region 生成测试数据,不在性能计算之内 11 for (var i = 0; i < count; i++) data[i] = random.Next(max); 12 #endregion 生成测试数据 13 14 #region 计算性能 15 Console.WriteLine("Zswang_0 开始运行"); 16 var tick = Environment.TickCount; 17 foreach (var i in Zswang_0(data, 4)) 18 { 19 Console.WriteLine(i); 20 } 21 Console.WriteLine("共耗时{0}毫秒", Environment.TickCount - tick); 22 #endregion 23 24 Console.ReadKey(); 25 } 26 算法一: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 static int[] Zswang_0(int[] data, int len) 2 { 3 // 计算每个数据出现的次数 4 var dict = new int[max]; 5 for (var i = 0; i < count; i++) dict[data[i]]++; 6 7 // 按出现的次数排序 8 var indexs = new int[max]; 9 for (var i = 0; i < max; i++) indexs[i] = i; // 获得完整的序号10 Array.Sort(indexs, delegate(int a, int b)11 {12 return dict[b] - dict[a];13 });14 /*15 for (var i = 0; i < 100; i++)16 {17 Console.WriteLine("{0}={1}", indexs[i], dict[indexs[i]]);18 }19 //*/20 var result = new int[len];21 for (var i = 0; i < len; i++) result[i] = indexs[i]; // 输出22 return result;23 }24 运行结果如下: 算法二: 1 static int[] ChrisAK_0(int[] data, int len) 2 { 3 // 计算每个数据出现的次数 4 int ncpu = Environment.ProcessorCount; 5 var dict = new int[ncpu][]; 6 int part = count / ncpu; 7 if (ncpu > 1) 8 { 9 Thread[] threads = new Thread[ncpu - 1];//每个cpu分配一个线程 10 for (var i = 1; i < ncpu; ++i) 11 { 12 dict[i] = new int[max];//分配线程用的计数器 13 var th = new Thread((baseidx) => 14 { 15 int bidx = (int)baseidx; 16 int start = bidx * part; 17 int end = start + part; 18 for (var j = bidx * part; j < end; j++) 19 { 20 ++dict[bidx][data[j]]; 21 } 22 }); 23 th.Start(i); 24 threads[i - 1] = th; 25 } 26 dict[0] = new int[max];//主线程开始计算自己的部分 27 for (var i = 0; i < part; i++) 28 { 29 dict[0][data[i]]++; 30 } 31 for (var i = 1; i < ncpu; ++i)// 等待其它线程结束 32 threads[i - 1].Join(); 33 for (var i = 0; i < max; ++i)//合并结果 34 { 35 int sum = 0; 36 for (var j = 1; j < ncpu; ++j) 37 sum += dict[j][i]; 38 dict[0][i] += sum; 39 } 40 for (int i = ncpu * part; i < count; ++i)//处理某些CPU内核个数无法整除的情况 41 dict[0][1]++; 42 } 43 else 44 { 45 for (var i = 0; i < part; i++)//单线程的情况. 46 { 47 dict[0][data[i]]++; 48 } 49 } 50 // 按出现的次数排序 51 var indexs = new int[max]; 52 for (var i = 0; i < max; i++) indexs[i] = i; // 获得完整的序号 53 Array.Sort(indexs, delegate(int a, int b) 54 { 55 return dict[0][b] - dict[0][a]; 56 }); 57 for (var i = 0; i < 50; i++) 58 { 59 Console.WriteLine("{0}={1}", indexs[i], dict[0][indexs[i]]); 60 } 61 var result = new int[len]; 62 for (var i = 0; i < len; i++) result[i] = indexs[i]; // 输出 63 return result; 64 } 65 运行结果如下: 算法三: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 static int[] Wuyi8808_0(int[] data, int len) 2 { 3 // 计算每个数据出现的次数 4 var dict = new int[max]; 5 for (var i = 0; i < count; i++) dict[data[i]]++; 6 // 奇怪,这里如果改用下一行,速度反而更慢: 7 // foreach (var x in data) dict[x]++; 8 9 // 按出现的次数排序10 var indexs = new int[max];11 for (var i = 0; i < max; i++) indexs[i] = i; // 获得完整的序号12 Array.Sort(dict, indexs); // 似乎这样就可以了,不过速度和伴水的是一样的13 //*14 for (var i = max - 1; i > max - 51; i--)15 {16 Console.WriteLine("{0,4}={1}", indexs[i], dict[i]);17 }18 //*/19 var result = new int[len];20 for (var i = 0; i < len; i++) result[i] = indexs[max - i - 1]; // 输出21 return result;22 }23 运行结果如下: 算法四: 1 static int[] sp1234_0(int[] data, int len) 2 { 3 var dict = new int[max]; 4 for (var i = 0; i < count; i++) dict[data[i]]++; 5 var indexs = new int[max]; 6 for (var i = 0; i < max; i++) indexs[i] = i; 7 int f = 0; 8 int t = indexs.Length - 1; 9 int m; 10 loop: 11 m = partition(dict, indexs, f, t); 12 if (m == len) 13 { 14 var result = new int[len]; 15 for (var i = 0; i < len; i++) result[i] = indexs[i]; 16 return result; 17 } 18 else if (m > len) 19 t = m - 1; 20 else 21 f = m + 1; 22 goto loop; 23 } 24 25 static int partition(int[] value, int[] data, int i, int j) 26 { 27 var x = i++; 28 var y = value[data[x]]; 29 int m; 30 begin: 31 while (i <= j && value[data[i]] >= y) 32 i++; 33 while (j >= i && value[data[j]] <= y) 34 j--; 35 if (i > j) 36 { 37 m = data[x]; 38 data[x] = data[j]; 39 data[j] = m; 40 return j; 41 } 42 43 m = data[i]; 44 data[i] = data[j]; 45 data[j] = m; 46 i++; 47 j--; 48 goto begin; 49 } 50 运行结果如下: 算法五: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 static int[] Wuyi8808_1(int[] data, int len) 2 { 3 int[] dict = new int[max]; 4 int[] indexs = new int[max]; 5 int[] result = new int[len]; 6 unsafe 7 { 8 fixed (int* a = data, b = dict, c = indexs) 9 {10 for (int i = 0; i < count; i++) b[a[i]]++;11 for (int i = 0; i < max; i++) c[i] = i;12 Array.Sort(dict, indexs);13 for (int i = 0; i < len; i++) result[i] = c[max - i - 1];14 }15 }16 return result;17 }18 运行结果如下: 算法六: 1 struct set 2 { 3 public int count; 4 public int key; 5 } 6 static int[] LCL_0(int[] data, int len) 7 { 8 // 计算每个数据出现的次数 9 set[] dict = new set[max]; 10 11 for (int i = 0; i < count; i++) 12 { 13 dict[data[i]].count++; 14 if (i < max) dict[i].key = i; 15 } 16 17 // 按出现的次数排序 18 Array.Sort(dict, delegate(set a, set b) 19 { 20 return b.count - a.count; 21 }); 22 23 int[] result = new int[len]; 24 for (int i = 0; i < len; i++) result[i] = dict[i].key; // 输出 25 return result; 26 } 27 运行结果如下: 算法七: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> 1 static int[] Lihanbing_0(int[] data, int len) 2 { 3 // 计算每个数据出现的次数 4 int[] dict = new int[max]; 5 for (int i = 0; i < count; i++) dict[data[i]]++; 6 7 // 按出现的次数排序 8 int[] result = new int[len], resultcount = new int[len]; 9 int tempResult = 0, tempResult2 = 0;10 int tempCount = 0, tempCount2 = 0;11 for (int i = 0; i < max; i++)12 {13 tempCount = dict[i];14 tempResult = i;15 for (int j = 0; j < len; j++)16 {17 if (tempCount <= resultcount[j])18 continue;19 tempCount2 = resultcount[j];20 tempResult2 = result[j];21 resultcount[j] = tempCount;22 result[j] = tempResult;23 tempCount = tempCount2;24 tempResult = tempResult2;25 26 }27 }28 return result;29 }30 运结结果如下: 本文转自温景良(Jason)博客园博客,原文链接:http://www.cnblogs.com/wenjl520/archive/2010/05/17/1737824.html,如需转载请自行联系原作者
关于Microsoft.CSharp.RuntimeBinder.RuntimeBinderException的异常一般来自于两种, 第一种: Predefined type 'Microsoft.CSharp.RuntimeBinder.Binder' is not defined or imported 解决它的办法是,直接在项目引用中添加 Micorsoft.Csharp 就可以了。 第二种: an exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' 引起这种异常的错误是因为匿名类型是不能跨程序集(assembly)的,第一种解决办法是使用 Expando ,第二个办法是在源程序集的AssemblyInfo.cs中加入: [assembly: InternalsVisibleTo("NameSpace1.SubNameSpace1")] 使用第二种方法后,匿名类型可以传递到目标程序集。 分类: ASP.NET 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6895434.html,如需转载请自行联系原作者
一、三种文件导入的方式比较: 类的前项声明@class、import、include: 1、采用@class 类名的方式,它会告诉编译器有这么一个类,目前不需要知道它内部的实例变量和方法是如何定义的,后面会告你,现在你就可以直接使用它,节约程序编译时间; 2、采用import方式,能避免重复导入同一类,它导入的不但这个类的所有的内容,而且使用它之前,编译器必须先对类的所有内容走一遍,就是先做预编译处理,这样比较耗费程序编译的时间。 3、采用include方式,不能避免重复导入的问题,但是它用在C/C++的代码编写中,因为#ifndef,#define,#endif这个是C/C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C/C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式 。 说明:在OC代码编写时,如果在一个类声明文件.h中,需要另一个类时,使用前项声明@class类名的方式是最好的选择,因为它省略了代码编译的时间,提高了效率;但是在实现文件.m文件中必须要用import方式,因为实现文件中要用到类的实例变量和方法,因此编译器必须先提前对类的内容做预编译处理。 二、atomic和nonatomic的区别: atomic: 设置成员变量的@property属性时,默认为atomic,提供多线程安全。因为多线程的缘故,所有的对象在操作成员变量时都是同步的,因此,为了防止一个对象在操作数据时还没有结束就被另一个对象抢走进行篡改,atomic为此提供了多线程安全机制,采用同步加锁的方式,来控制进程的归属权。 即: {lock} //第一个对象获取操作数据后,给自己的进程加锁,别人在我没有释放锁之前就无法再去抢夺了。 if (property != newValue) { [property release]; property = [newValue retain]; } {unlock}//该对象操作完数据后,就释放了同步锁,别人就可以获取这个数据进行自己的操作了。 说明:锁必须是唯一的。 nonatomic: 设置成员变量的@property属性时,设置nonatomic,禁止多线程。这就直接消除了数据操作过程中被被另一方篡改的可能性。如果不是多线程,一般就采用这种设置。 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Objective-C 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4887943.html,如需转载请自行联系原作者
弹出窗控制器:UIPopoverController 截图: 实质:就是将内容控制器包装成popoverController的形式,然后在模态出来,必须给定指向目标(target、frame)。 功能:它是ipd特有的特性,不适用iphone,用来点击一个按钮时,弹出一个窗口以显示一些信息。 如果没有使用passthroughViews属性设置过滤控件,那么点击屏幕的任意区域都可以关闭弹出窗;可是,如果设置了passthroughViews属性过滤了按钮的父视图和显示区域控件,那么点击屏幕任何区域都是无效的,只有点击按钮才会与用户发生交互。 介绍:它是直接继承自NSObject的控件,并不是UIViewController的子类,所以,它不具备可视化的条件。可是,既然它是弹出窗,就必须具有可视效果,如何实现呢?其实,它是通过创建第三方控制器来实现的,即内容控制器,该控制器继承于UIViewController,将其设置为UIPopoverController的内容控制器即可。 设置内容的尺寸有2种方法: 方法一: @property (nonatomic) CGSize popoverContentSize; - (void)setPopoverContentSize:(CGSize)size animated:(BOOL)animated; 以上方法和属性都是UIPopoverController的 方法二: 内容控制器可以自行设置自己在popover中显示的尺寸 p在iOS 7之前 @property (nonatomic,readwrite) CGSize contentSizeForViewInPopover; 从iOS 7开始 @property (nonatomic) CGSize preferredContentSize; 以上属性都是UIViewController的 类介绍: 箭头方向枚举: typedef NS_OPTIONS(NSUInteger, UIPopoverArrowDirection) { UIPopoverArrowDirectionUp = 1UL << 0, //箭头往上指向按钮 UIPopoverArrowDirectionDown = 1UL << 1, //箭头往下指向按钮 UIPopoverArrowDirectionLeft = 1UL << 2, //箭头往左指向按钮 UIPopoverArrowDirectionRight = 1UL << 3, //箭头往右指向按钮 UIPopoverArrowDirectionAny = UIPopoverArrowDirectionUp | UIPopoverArrowDirectionDown | UIPopoverArrowDirectionLeft | UIPopoverArrowDirectionRight, //箭头方向自适应 UIPopoverArrowDirectionUnknown = NSUIntegerMax //箭头方向未知 }; @interface UIPopoverController : NSObject <UIAppearanceContainer> 属性: //弹出框控制器代理 @property (nonatomic, assign) id <UIPopoverControllerDelegate> delegate; //内容控制器 @property (nonatomic, retain) UIViewController *contentViewController; //弹出窗是否可见的(只读属性) @property (nonatomic, readonly, getter=isPopoverVisible) BOOL popoverVisible; //弹出窗的箭头方向 @property (nonatomic, readonly) UIPopoverArrowDirection popoverArrowDirection; //过滤视图控件,即该数组中视图不可以与用户进行交互,但是其他区域的控件是可以的,一般选择按钮本身 @property (nonatomic, copy) NSArray *passthroughViews; //弹出窗中内容区域大小 @property (nonatomic) CGSize popoverContentSize; //弹出窗背景颜色 @property (nonatomic, copy) UIColor *backgroundColor ; //弹出窗偏移布局 @property (nonatomic, readwrite) UIEdgeInsets popoverLayoutMargins ; //弹出窗后台视图类 @property (nonatomic, readwrite, retain) Class popoverBackgroundViewClass; 方法: //创建弹出窗控制器实例,内容控制器为参数 - (instancetype)initWithContentViewController:(UIViewController *)viewController; //设置弹出窗控制器的内容控制器 - (void)setContentViewController:(UIViewController *)viewController animated:(BOOL)animated; //设置弹出窗内容区域的大小 - (void)setPopoverContentSize:(CGSize)size animated:(BOOL)animated; //弹出窗围绕着某一块特定区域显示(箭头指定那块特定区域) - (void)presentPopoverFromRect:(CGRect)rect inView:(UIView *)view permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated; //弹出窗围绕着一个UIBarButtonItem显示(箭头指定那个UIBarButtonItem) - (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections animated:(BOOL)animated; //关闭弹出窗 - (void)dismissPopoverAnimated:(BOOL)animated; @end 协议: @protocol UIPopoverControllerDelegate <NSObject> @optional //弹出窗将要弹出时触发的方法 - (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController; //弹出窗弹出时触发的方法 - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController; //弹出窗将要复位到某一特定区域时触发的方法 - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view; @end 演示实例如下: 第一种方式:弹出窗围绕着某一块特定区域显示(箭头指定那块特定区域),我使用按钮UIButton作为特定区域。 1.首先创建一个内容控制器ContentViewController,它直接继承自UIViewcontroller 2.所有的文件截图为: 3.在内容控制器ContentViewController.m文件使用UIViewController的preferredContentSize属性设置显示区域大小 #import "ContentViewController.h" @interface ContentViewController () @end @implementation ContentViewController - (void)viewDidLoad { [super viewDidLoad]; //设置视图颜色 self.view.backgroundColor = [UIColor redColor]; //设置内容控制器显示局域大小 self.preferredContentSize = CGSizeMake(100, 200); } @end 4.在当前控制器ViewController.m文件中进行主要代码操作: //声明属性 #import "ViewController.h" #import "ContentViewController.h" @interface ViewController () @property (strong,nonatomic)UIPopoverController *popoverVC; //声明弹出窗控制器 @property (strong,nonatomic)ContentViewController *contentVC; //声明内容控制器 @end //创建UIButton按钮控件,并添加打开弹出窗事件 - (void)viewDidLoad { [super viewDidLoad]; //创建按钮 UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 40, 40)]; //设置按钮标题 [button setTitle:@"打开" forState:UIControlStateNormal]; //设置按钮背景颜色 button.backgroundColor = [UIColor purpleColor]; //添加事件,用来打开弹出窗 [button addTarget:self action:@selector(Open:) forControlEvents:UIControlEventTouchUpInside]; //添加按钮到视图中 [self.view addSubview:button]; } //实现按钮事件 -(void)Open:(UIButton *)sender { if (!self.popoverVC.isPopoverVisible) { //设置内容控制器 self.contentVC = [[ContentViewController alloc]init]; //创建UIPopoverController(将当前视图控制器设置为内容控制器) self.popoverVC = [[UIPopoverController alloc]initWithContentViewController:self.contentVC]; //设置一直显示区域,此时点击视图和弹出窗与用户不会发生任何交互,只有按钮能与用户交互 self.popoverVC.passthroughViews = @[self.view,sender]; //打开popoverVC控制器,设置箭头方向为自适应 [self.popoverVC presentPopoverFromRect:sender.bounds inView:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } else { //关闭popoverVC控制器 [self.popoverVC dismissPopoverAnimated:YES]; } } //演示结果截图 开始时 : 点击按钮时:(因为设置了passthroughViews,此时,仅有按钮能与用户交互,点击可以关闭弹出窗,点击其他区域没有任何效果) 第二种方式:弹出窗围绕着UIBarButtonItem显示,箭头指定UIBarButtonItem 1.首先创建一个内容控制器ContentViewController,它直接继承自UIViewcontroller 2.所有的文件截图为: 3.在当前控制器ViewController.m文件中进行主要代码操作: //声明属性 #import "ViewController.h" #import "ContentViewController.h" @interface ViewController () @property (strong,nonatomic)UIPopoverController *popoverVC; //声明弹出窗控制器 @property (strong,nonatomic)ContentViewController *contentVC;//声明内容控制器 @end //创建UIBarButtonItem,并添加打开弹出窗事件 - (void)viewDidLoad { [super viewDidLoad]; //创建工具栏控件 UIToolbar *toolBar = [[UIToolbar alloc]init]; toolBar.frame = CGRectMake(0, 0, self.view.frame.size.width,40); //创建工具栏目 UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"打开" style:UIBarButtonItemStylePlain target:self action:@selector(Open:)]; UIBarButtonItem *fixItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; fixItem.width = 200; //将工具栏目添加到工具栏 [toolBar setItems:@[fixItem,item]]; //将工具栏添加到控制器视图中 [self.view addSubview:toolBar]; } //实现按钮事件,其中通过弹出窗的popoverContentSize属性设置弹出窗内容区域的颜色 #pragma mark -打开弹窗 -(void)Open:(UIBarButtonItem *)sender { if (!self.popoverVC.popoverVisible) { //创建内容控制器 self.contentVC = [[ContentViewController alloc]init]; //创建popoverVC self.popoverVC = [[UIPopoverController alloc]initWithContentViewController:self.contentVC]; //设置内容显示区域大小和背景颜色 //self.popoverVC.popoverContentSize = CGSizeMake(100, 200); [self.popoverVC setPopoverContentSize:CGSizeMake(100, 200) animated:YES]; self.popoverVC.backgroundColor = [UIColor redColor]; //设置一直显示的区域,过滤了当前视图和内容区域,此时只有UIBarButtonItem能与用户进行交互 self.popoverVC.passthroughViews = @[self.view,sender]; //打开弹窗口 [self.popoverVC presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } else { //关闭弹窗口 [self.popoverVC dismissPopoverAnimated:YES]; } } //演示结果截图: 开始时: 点击按钮时:(因为设置了passthroughViews,此时,仅有按钮能与用户交互,点击可以关闭弹出窗,点击其他区域没有任何效果) 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: iOS高级 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4897146.html,如需转载请自行联系原作者
多年从事框架设计开发使我有了一种强迫症,那就是见不得一个应用里频繁地出现重复的代码。之前经常Review别人的代码,一看到这样的程序,我就会想如何将这些重复的代码写在一个地方,然后采用“注入”的方式将它们放到需要的程序中。我们知道AOP是解决这类问题最理想的方案。为此,我自己写了一个AOP框架,该框架被命名为Dora.Interception。Dora.Interception已经在GitHub上开源,如果有兴趣的朋友想下载源代码或者阅读相关文档,可以访问GitHub地址:https://github.com/jiangjinnan/Dora。Demo源代码下载地址:http://files.cnblogs.com/files/artech/Dora.Interception.Demo.rar 目录 一、Dora, 为什么叫这个名字? 二、Dora.Interception的设计目标 三、以怎样的方式使用Dora.Interception 四、如何定义一个Interceptor 五、定义InterceptorAttribute 六、应用InterceptorAttribute 七、以Dependency Injection的形式提供Proxy 一、Dora, 为什么叫这个名字? 其实我最早的想法是创建一个IoC框架,并将它命名为Doraemon(哆啦A梦),因为我觉得一个理想的IoC Container就像是机器猫的二次元口袋一样能够提供给你期望的一切服务对象。后来觉得这名字太长,所以改名为Dora。虽然Dora这个名字听上去有点“娘”,并且失去了原本的意思,但是我很喜欢这个单词的一种释义——“上帝的礼物”之一。在接触了.NET Core的时候,我最先研究的就是它基于ServiceCollection和ServiceProvider的Dependency Injection框架,虽然这个框架比较轻量级,但是能够满足绝大部分项目的需求,所以我放弃了初衷。不过我依然保留了Dora这个开源项目名,并为此购买了一个域名(doranet.org),我希望将我多年的一些想法以一系列开源框架的形式实现出来,Dora.Interception就是Dora项目的第一个基于AOP的框架。 二、Dora.Interception的设计目标 我当初在设计Dora.Interception框架时给自己确定的几个目标: Dora.Interception一个基于运行时(Run Time),而不是针对编译时(Compile Time)的AOP框架。它通过在运行时动态创建代理对象(Proxy)来封装目标对象(Target),并自动注入应用的拦截器(Interceptor),而不是在编译时帮助你生成一个Proxy类型。 Dora.Interception需要采用一种优雅的方式来定义和应用Interceptor。 能够与.NET Core的Dependency Injection框架无缝集成 能够整合其他AOP框架。实际上Dora.Interception并没有自行实现最底层的“拦截”机制,我使用的是Castle的DynamicProxy。如果有其他的选择,我们可以很容易地将它引入进来。 三、以怎样的方式使用Dora.Interception Dora.Interception目前的版本为1.1.0,由如下两个NuGet包来承载,由于Dora.Interception.Castle依赖于Dora.Interception,所以安装后者即可。 Dora.Interception: 提供基本的API Dora.Interception.Castle: 提供基于Castle(DynamicProxy)的拦截实现 四、如何定义一个Interceptor 接下来我们通过一个简单的实例来说明一下如何采用“优雅”的方式来定义一个Interceptor类型。我们即将定义的这个CacheInterceptor可以应用到某个具有返回值的方法上实现针对返回值的缓存。如果应用了这个Interceptor,它根据传入的参数对返回的值实施缓存。如果后续调用传入了相同的参数,并且之前的缓存尚未过期,缓存的结果将直接作为方法的返回值,从而避免了针对目标方法的重复调用。针对的缓存功能实现在如下这个CacheInterceptor类型中,可以看出针对的缓存是利用MemoryCache来完成的。 1: public class CacheInterceptor 2: { 3: private readonly InterceptDelegate _next; 4: private readonly IMemoryCache _cache; 5: private readonly MemoryCacheEntryOptions _options; 6: 7: public CacheInterceptor(InterceptDelegate next, IMemoryCache cache, IOptions<MemoryCacheEntryOptions> optionsAccessor) 8: { 9: _next = next; 10: _cache = cache; 11: _options = optionsAccessor.Value; 12: } 13: 14: public async Task InvokeAsync(InvocationContext context) 15: { 16: if (!context.Method.GetParameters().All(it => it.IsIn)) 17: { 18: await _next(context); 19: } 20: 21: var key = new Cachekey(context.Method, context.Arguments); 22: if (_cache.TryGetValue(key, out object value)) 23: { 24: context.ReturnValue = value; 25: } 26: else 27: { 28: await _next(context); 29: _cache.Set(key, context.ReturnValue, _options); 30: } 31: } 32: public class CacheKey {...} 33: } CacheInterceptor体现了一个典型的Interceptor的定义方式: Interceptor类型无需实现任何的接口,我们只需要定义一个普通的公共实例类型即可。 Interceptor类型必须具有一个公共构造函数,并且该构造函数的第一个参数的类型必须是InterceptDelegate,后者代表的委托对象会帮助我们调用后一个Interceptor或者目标方法(如果当前Interceptor已经是最后一个了)。 上述这个构造函数可以包含任意的参数(比如CacheInterceptor构造函数中的cache和optionsAccessor)。这些参数可以直接利用.NET Core的Dependency Injection的方式进行注册,对于没有注册的参数需要在应用该Interceptor的时候显式提供。 拦截功能实现在约定的InvokeAsync的方法中,这是一个返回类型为Task的异步方法,它的第一个参数类型为InvocationContext,代表当前方法调用的上下文。我们可以利用这个上下文对象得到Proxy对象和目标对象,代表当前调用方法的MethodInfo对象,以及传入的输入参数等。除此之外,我们也可以利用这个上下文直接设置方法的返回值或者输出参数。 这个InvokeAsync方法可以包含任意后续参数,但是要求这些参数预先以Dependency Injection的形式进行注册。这也是我没有定义一个接口来表示Interceptor的原因,因为这样就不能将依赖的服务直接注入到InvokeAsync方法中了。 当前Interceptor是否调用后续的Interceptor或者目标方法,取决于你是否调用构造函数传入的这个InterceptDelegate委托对象。 由于依赖的服务对象(比如CacheInterceptor依赖IMemoryCache 和IOptions<MemoryCacheEntryOptions>对象)可以直接注入到InvokeAsync方法中,所以上述这个CacheInterceptor也可以定义成如下的形式 1: public class CacheInterceptor 2: { 3: private readonly InterceptDelegate _next; 4: public CacheInterceptor(InterceptDelegate next) 5: { 6: _next = next; 7: } 8: 9: public async Task InvokeAsync(InvocationContext context, IMemoryCache cache, IOptions<MemoryCacheEntryOptions> optionsAccessor) 10: { 11: if (!context.Method.GetParameters().All(it => it.IsIn)) 12: { 13: await _next(context); 14: } 15: 16: var key = new Cachekey(context.Method, context.Arguments); 17: if (cache.TryGetValue(key, out object value)) 18: { 19: context.ReturnValue = value; 20: } 21: else 22: { 23: await _next(context); 24: _cache.Set(key, context.ReturnValue, optionsAccessor.Value); 25: } 26: } 27: } 五、定义InterceptorAttribute 我们采用Attribute的形式来将对应的Intercepor应用到某个类型或者方法上,每个具体的Interceptor类型都具有对应的Attribute。这样的Attribute直接继承基类InterceptorAttribute。如下这个CacheReturnValueAttribute就是上面这个CacheInterceptor对应的InterceptorAttribute。 1: [AttributeUsage(AttributeTargets.Method)] 2: public class CacheReturnValueAttribute : InterceptorAttribute 3: { 4: public override void Use(IInterceptorChainBuilder builder) 5: { 6: builder.Use<CacheInterceptor>(this.Order); 7: } 8: } 具体的InterceptorAttribute只需要重写Use方法将对应的Interceptor添加到Interceptor管道之中,这个功能可以直接调用作为参数的InterceptorChainBuilder对象的泛型方法Use<TInterceptor>来实现。对于这个泛型方法来说,泛型参数类型代表目标Interceptor的类型,而第一个参数表示注册的Interceptor在整个管道中的位置。如果创建目标Interceptor而调用的构造函数的参数尚未采用Dependency Injection的形式注册,我们需要在这个方法中提供。对于CacheInterceptor依赖的两个对象(IMemoryCache 和IOptions<MemoryCacheEntryOptions>)都可以采用Dependency Injection的形式注入,所以我们在调用Use<CacheInterceptor>方法是并不需要提供这个两个参数。 假设我们定义一个ExceptionHandlingInterceptor来实施自动化异常处理,当我们在创建这个Interceptor的时候需要提供注册的异常处理类型的名称,那么我们需要采用如下的形式来定义对应的这个IntercecptorAttribute。如下面的代码片段所示,我们在调用Use<ExceptionHandlingInterceptor>方法的时候就需要显式指定这个策略名称。 1: [AttributeUsage(AttributeTargets.Method|AttributeTargets.)] 2: public class HandleExceptionAttribute : InterceptorAttribute 3: { 4: public string ExceptionPolicy {get;} 5: public string HandleExceptionAttribute(string exceptionPolicy) 6: { 7: this.ExceptionPolicy = exceptionPolicy; 8: } 9: public override void Use(IInterceptorChainBuilder builder) 10: { 11: builder.Use<ExceptionHandlingInterceptor>(this.Order,this.ExceptionPolicy); 12: } 13: } 有的时候,IntercecptorAttribute在注册对应Interceptor的时候需要使用到应用到当前方法或者类型上的其他Attribute。举个简单的例子,上述的这个HandleExceptionAttribute实际上是自动提供异常处理策略名称,假设异常处理系统自身使用另外一个独立的ExceptionPolicyAttribute采用如下的形式来提供这个策略。 1: public class Foobar 2: { 3: [ExceptionPolicy("DefaultPolicy") 4: public void Invoke() 5: { 6: ... 7: } 8: } 这个问题很好解决,因为InterceptorAttribute自身提供了应用到目标方法或者类型上的所有Attribute,所以上述这个HandleExceptionAttribute可以采用如下的定义方式。 1: [AttributeUsage(AttributeTargets.Method)] 2: public class HandleExceptionAttribute : InterceptorAttribute 3: { 4: public override void Use(IInterceptorChainBuilder builder) 5: { 6: ExceptionPolicyAttribute attribute = this.Attributes.ofType<ExceptionPolicyAttribute>().First(); 7: builder.Use<Exception>(this.Order, attribute.ExceptionPolicy); 8: } 9: } 六、应用InterceptorAttribute Interceptor通过对应的InterceptorAttribute被应用到某个方法或者类型上,我们在应用InterceptorAttribute可以利用其Order属性确定Interceptor的排列(执行)顺序。如下面的代码片段所示, HandleExceptionAttribute和CacheReturnValueAttribute分别被应用到Foobar类型和Invoke方法上,我要求ExceptionHandlingInterceptor能够处理CacheInterceptor抛出的异常, 那么前者必须由于后者执行,所以我通过Order属性控制了它们的执行顺序。值得一提的是,目前我们支持两个拦截机制,一种是基于接口,另一种是基于虚方法。如果采用基于接口的拦截机制,我要求InterceptorAttribute应用在实现类型或者其方法上,应用在接口和其方法上的InterceptorAttribute将无效。 1: [HandleException("defaultPolicy", Order = 1)] 2: public class Foobar: IFoobar 3: { 4: [CacheReturnValue(this.Order = 2)] 5: public Data LoadData() 6: { 7: ... 8: } 9: } 如果我们在类型上应用了某个InterceptorAttribute,但是对应的Interceptor却并不希望应用到某个方法中,我们可以利用NonInterceptableAttribute采用如下的形式将它们屏蔽, 1: [CacheReturnValue] 2: public class Foobar 3: { 4: ... 5: [NonInterceptable(typeof(CacheReturnValueAttribute)] 6: public Data GetRealTypeData() 7: {...} 8: } 七、以Dependency Injection的形式提供Proxy 我们知道应用在目标类型或者其方法上的Interceptor能够生效,要求方法调用针对的是封装目标对象的Proxy对象,换句话说我们希望提供的对象是一个Proxy而不是目标对象。除此之外,我们在上面的设计目标已经提到过,我们希望这个AOP框架能够与.NET Core的Dependency Injection框架进行无缝集成,所以现在的问题变成了:如何让Dependency Injection的ServiceProvider提供的是Proxy对象,而不是目标对象。我提供的两种方案来解决这个问题,接下来我们通过一个ASP.NET Core MVC应用来举例说明。 为了能够使用上面提供的CacheInterceptor并且能够以很直观的方式感受到缓存的存在,我定义了如下这个表示系统时钟的ISystemClock接口和具体实现类型SystemClock。从如下的代码片段可以看出,GetCurrentTime方法总是返回实时的时间,但是由于应用了CaheReturnValueAttribute,如果CacheInterceptor生效,返回的时间在缓存过期之前总是相同的。 1: public interface ISystomClock 2: { 3: DateTime GetCurrentTime(); 4: } 5: 6: public class SystomClock : ISystomClock 7: { 8: [CacheReturnValue] 9: public DateTime GetCurrentTime() 10: { 11: return DateTime.UtcNow; 12: } 13: } 我们在HomeController中以构造器注入的方式来使用ISystemClock。在默认情况下,如果我们注入的类型ISystemClock接口,那么毫无疑问,那么GetCurrentTime方法调用的就是SystemClock对象本身,所以根本不可能起到缓存的作用。所以我们将注入类型替换成IInterceptable<ISystomClock>,后者的Proxy属性将会返回我们希望的Proxy对象。 1: public class HomeController : Controller 2: { 3: private readonly ISystomClock _clock; 4: public HomeController(IInterceptable<ISystomClock> clockAccessor) 5: { 6: _clock = clockAccessor.Proxy; 7: } 8: 9: [HttpGet("/")] 10: public async Task Index() 11: { 12: this.Response.ContentType = "text/html"; 13: await this.Response.WriteAsync("<html><body><ul>"); 14: for (int i = 0; i < 5; i++) 15: { 16: await this.Response.WriteAsync($"<li>{_clock.GetCurrentTime()}({DateTime.UtcNow})</li>"); 17: await Task.Delay(1000); 18: } 19: await this.Response.WriteAsync("</ul><body></html>"); 20: } 21: } 当然我们需要注册Dora.Interception一些必须的服务,这些服务采用如下的形式通过调用扩展方法AddInterception来实现。 1: public class Startup 2: { 3: public void ConfigureServices(IServiceCollection services) 4: { 5: services 6: .AddScoped<ISystomClock, SystomClock>() 7: .AddInterception(builder=>builder.SetDynamicProxyFactory()) 8: .AddMvc(); 9: } 10: public void Configure(IApplicationBuilder app) 11: { 12: app.UseMvc(); 13: } 14: } 虽然IInterceptable<T>能够解决Proxy的提供问题,但是这种编程模式其实是很不好的。理想的编程模式应该是:依赖某个服务就注入对应的服务接口就可以。这个问题其实也好解决,我们首先将HomeController还原成典型的编程模式: 1: public class HomeController : Controller 2: { 3: private readonly ISystomClock _clock; 4: public HomeController(ISystomClock clock) 5: { 6: _clock = clock; 7: } 8: 9: [HttpGet("/")] 10: public async Task Index() 11: { 12: this.Response.ContentType = "text/html"; 13: await this.Response.WriteAsync("<html><body><ul>"); 14: for (int i = 0; i < 5; i++) 15: { 16: await this.Response.WriteAsync($"<li>{_clock.GetCurrentTime()}({DateTime.UtcNow})</li>"); 17: await Task.Delay(1000); 18: } 19: await this.Response.WriteAsync("</ul><body></html>"); 20: } 21: } 接下来我们只需要修改Startup的ConfigureServices的两个地方同样达到相同的目的。如下面的代码片段所示,我们让ConfigureServices返回一个IServiceProvider对象,这个对象直接调用我们定义的扩展方法BuilderInterceptableServiceProvider来创建。 1: public class Startup 2: { 3: public IServiceProvider ConfigureServices(IServiceCollection services) 4: { 5: services 6: .AddScoped<ISystomClock, SystomClock>() 7: .AddMvc(); 8: return services.BuilderInterceptableServiceProvider(builder => builder.SetDynamicProxyFactory()); 9: } 10: 11: public void Configure(IApplicationBuilder app) 12: { 13: app.UseMvc(); 14: } 15: } 对于上述的两种编程模式,运行程序后浏览器上都会呈现出相同的时间: 分类: ASP.NET 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6796926.html,如需转载请自行联系原作者
译自:http://blogs.msdn.com/kushals/archive/2010/04/07/wf3-design-time-performance-ii.aspx 最近,一个客户的设计器在启动的时候非常慢,他的项目只是一个中型的,因此性能的下降是令人非常费解的。 为了找出是哪里的问题,我使用附加到进程方式将含有工作流设计器的实例附加到VS中,并且打开所有异常,然后我尝试启动工作流设计器发现… 我得到了数百个文件IO找不到的异常,难怪性能会这么慢。 我尝试重新编译项目,并检查客户是否少了什么。但是项目生成成功,没有任何错误。是什么导致设计器的性能这么慢呢? 问题如下:如果工作流中的活动依赖程序集A(使用其中的一些类型),程序集A依赖于程序集B。在设计时,当你装载工作流的时候,活动实例就会被创建。在活动实例创建其间,CLR想要确保活动程序集以及所有他依赖的程序集都能被装载(A和B),为了这个目的,VS的类型解析服务会根据活动程序集来查找A和B,然后还会查找GAC。 如果在这些路径中仍然找不到,他将会使用程序集A和B的部分名字执行Assembly.Load动作。这就意味着如果程序集的1.0.0.版本没有找到,他将尝试1.0.0.1版本。同样的,如果culture=neutral的程序集找不到,他会查找Specific Windows或Framework Culture。这仍然会根据CLR装载程序集的算法有不同的变化。因此,你可以想象每个程序集都有大量的文件没有找到,同样当需要多个程序集的时候在工作流设计时创作环境中就会无法找到。 因此,除了在前面文章的Tips,请使用附加到进程方式将含有工作流设计器的实例附加到VS中,并且观察是否有大量的文件无法找到的异常。如果有,请确保必要的程序集在VS的探测路径中,并查看是否有性能上的提高。 请关注后续文章。 本文转自生鱼片博客园博客,原文链接:http://www.cnblogs.com/carysun/archive/2010/04/08/WF3-designtime-performance-II.html,如需转载请自行联系原作者
maven打包出错: Failed to clean project: Failed to delete 出现这种错误,通常是由于您已启动了另一个tomcat 进程,导致报错,关闭tomcat进程即可。 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6994310.html,如需转载请自行联系原作者
几种字符输入函数的区别: 1、getche()函数:用于从键盘读入一个字符并显示,然后直接执行下一条语 句。 2、getch()函数:用于从键盘中读入一个字符,但不显示在屏幕上, 然后执行下一条语句。 3、getchar()函数:用于从键盘读入一个字符,然后等待输入是否结束, 如果用户按下enter键,则执行下一条语句。 4、putchar()函数:用于将字符常量或者字符变量输出在屏幕上, 并自动换行。 ASCII值对应字符类型: 大写字母:65~90 小写字母:97~122 数字类型:48~57 特殊字符:0~47、58~64、91~96 字符串的拷贝:strcpy(str1,str2) 1、strcpy(str1,str2):将源字符串str2地址拷贝到目的字符串地 址str1。 2、strcpy(字符数组名,字符常量):strcpy(str,"1234”) 注意:(1)不能将字符数组地址拷贝到字符常量,运行时会出现bus error总线错误 例如:strcpy(“hello world”,str) (2)目的字符串长度必须大于源字符串的长度,不然会出现内存溢出。 改进版字符串拷贝:strncpy(str1,str2,n_bytes) 控制着拷贝的字节数 字符串的比较:strcmp(str,str2) 返回值大于0; >; 返回值等于0,==;返回值小于0,<。 3、自定义字符串比较,比较条件:str1[i]!=‘\0 && str2[i]!=0 && str1[i]==str2[i] 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: C 本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4887522.html,如需转载请自行联系原作者
所谓模式,就是在某种场景下,一类问题及其解决方案的总结归纳。生产消费者模式与订阅发布模式是使用消息中间件时常用的两种模式,用于功能解耦和分布式系统间的消息通信,以下面两种场景为例: 数据接入 假设有一个用户行为采集系统,负责从App端采集用户点击行为数据。通常会将数据上报和数据处理分离开,即App端通过REST API上报数据,后端拿到数据后放入队列中就立刻返回,而数据处理则另外使用Worker从队列中取出数据来做,如下图所示。 这样做的好处有:第一,功能分离,上报的API接口不关心数据处理功能,只负责接入数据;第二,数据缓冲,数据上报的速率是不可控的,取决于用户使用频率,采用该模式可以一定程度地缓冲数据;第三,易于扩展,在数据量大时,通过增加数据处理Worker来扩展,提高处理速率。这便是典型的生产消费者模式,数据上报为生产者,数据处理为消费者。 事件分发 假设有一个电商系统,那么,用户“收藏”、“下单”、“付款”等行为都是非常重要的事件,通常后端服务在完成相应的功能处理外,还需要在这些事件点上做很多其他处理动作,比如发送短信通知、记录用户积分等等。我们可以将这些额外的处理动作放到每个模块中,但这并不是优雅的实现,不利于功能解耦和代码维护。 我们需要的是一个事件分发系统,在各个功能模块中将对应的事件发布出来,由对其感兴趣的处理者进行处理。这里涉及两个角色:A对B感兴趣,A是处理者,B是事件,由事件处理器完成二者的绑定,并向消息中心订阅事件。服务模块是后端的业务逻辑服务,在不同的事件点发布事件,事件经过消息中心分发给事件处理器对应的处理者。整个流程如下图所示。这边是典型的订阅发布模式。 可以看到,生产消费者模式与订阅发布模式都离不开消息中间件来作为消息中转站,开源的消息中间件有很多,各有优劣。本文将重点探讨RabbitMQ的特性,以及如何实现上述的两种场景。 RabbitMQ核心概念 如果你只是想使用一下RabbitMQ,那么参考官方教程修改一下就可以跑起来了,很简单。如果你有一些概念上的疑惑,不妨与笔者一起来总结一下RabbitMQ的核心概念。 通信方式 RabbitMQ是基于AMQP协议来实现的消息中间件。AMQP,类似于HTTP协议,也是一个应用层的协议,网络层使用TCP来通信。因此,RabbitMQ也是典型的C-S模型,准确地说是C-S-C模型,因为伴随着RabbitMQ的使用,总是会有Producer与Consumer两个Client和一个Broker Server。 Client要与Server进行通信,就必须先建立连接,RabbitMQ中有Connection与Channel两个概念,前者就是一个TCP连接,后者是在这个连接上的虚拟概念,负责逻辑上的数据传递,因此,为了节省资源,一般在一个客户端中建立一个Connection,每次使用时再分配一个Channel即可。 消息体 Message是RabbitMQ中的消息体概念。类似HTTP传输中,有header和body两部分数据,Message中也有Attributes和Payload两部分数据,前者是一些元信息,后者是传递的消息数据实体。 消息投递 Exchange、Queue与Routing Key三个概念是理解RabbitMQ消息投递的关键。RabbitMQ中一个核心的原则是,消息不能直接投递到Queue中。Producer只能将自己的消息投递到Exchange中,由Exchange按照routing_key投递到对应的Queue中,具体的架构参见下图。细细品味就会体会到这样设计的精妙之处。 那么,具体实现时,如何完成这三者关系的绑定?总结起来是两点:第一,在Consumer Worker中,声明自己对哪个Exchange感兴趣,并将自己的Queue绑定到自己感兴趣的一组routing_key上,建立相应的映射关系;第二,在Producer中,将消息投递一个Exchange中,并指明它的routing_key。由此可见,Queue这个概念只是对Consumer可见,Producer并不关心消息被投递到哪个Queue中。 看过RabbitMQ的”Hello World”教程的童鞋可能会发现在那里面的图中并没有看到Exchange和routing_key的踪迹,但这并不意味着RabbitMQ可以支持直接将消息投递到Queue中,而是在内部使用了默认的Exchange和routing_key了。默认情况下,RabbitMQ使用名称为“amq.direct”的Direct Exchange,routing_key默认名字与Queue保持一致。 搞清楚上述概念,就不难理解Exchange的四种类型了。Direct、Fanout、Topic、Headers,区别在于如何将消息从Exchange投递到Queue中。Direct使用具体的routing_key来投递;Fanout则忽略routing_key,直接广播给所有的Queue;Topic是使用模糊匹配来对一组routing_key进行投递;Headers也是忽略routing_key,使用消息中的Headers信息来投递。 消息可靠性 不同于HTTP的同步访问,RabbitMQ中,Producer并不知道消息是否被可靠地投递到了Consumer中处理。那么,RabbitMQ是如何保证消息的可靠投递?主要是两点:第一,消息确认机制。Consumer处理完消息后,需要发送确认消息给Broker Server,可以选择“确认接收”、“丢弃”、“重新投递”三种方式。如果Consumer在Broker Server收到确认消息之前挂了,Broker Server便会重新投递该消息。第二,可以选择数据持久化,这样即使RabbitMQ重启,也不会丢失消息。 生产消费者模式 搞清楚了RabbitMQ的核心概念,要针对特定的场景来设计使用方案就很简单了,基本上就是上述RabbitMQ架构图的变迁。让我们先来看看文章开头提到的“数据接入”场景,如何实现生产消费者模式。 这里增加了一下场景复杂度:对于上报的数据,如果是special的行为,需要优先处理。从上图可以看到,数据上报端负责将数据投递到RabbitMQ对应的Exchange,并指明routing_key是common还是special。数据处理端,可以根据情况启多个Woker来消费数据,但至少需要两个,一个用来处理common数据,一个用来处理special的数据。注意:当需要增加多个Worker来消费同一类数据时,需要保持Queue名字一致,比如上面的Common数据。 订阅发布模式 再来看“事件分发”的场景,架构如下图所示,使用event name/id来作为RabbitMQ的routing key的名字。Event Processor 01对event 01 和event 02感兴趣,则在启动Consumer Worker时,将自己的Queue绑定到这两个routing key上即可,其他Event Processor也是如此,这样便完成了事件的订阅。当有事件发布时,消息便会按照event name/id被投递到对应的Queue中。 由此可见,在不同的应用中,变化的只是routing_key与Consumer Queue的绑定关系,在充分理解RabbitMQ的核心概念后处理这些应该也是得心应手了。 出处:http://blog.csdn.net/zwgdft/article/details/53561277 分类: 设计模式 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/7156641.html,如需转载请自行联系原作者
最近开发了一个小软件。由于需要打包。网上找了一些资料。然后整合了起来。希望对大家有所帮助。不全面请见谅。 打包控件 InstallShield-Limited-Edition 下面是注册地址 http://learn.flexerasoftware.com/content/IS-EVAL-InstallShield-Limited-Edition-Visual-Studio?lang=1033&ver=ult 用你的邮箱注册,会免费发注册码的,不需要破解 也可以通过CSDN下载包 http://download.csdn.net/download/u013054786/10048455 如果没有安装打包软件。 通过包进行安装控件 具体步骤: 1.新建了一个基本的三层项目,用来测试打包 2.根据实际情况,填写程序基本信息 3.这一步非常重要,看下图 : 1)设置为简体中文,否则安装路径有中文的话就会出问题 2)设置默认安装路径 3)修改默认字体 4)每次升级,重新打包,只需要点击这一行右侧的“...”按钮,就会重新生成Code,安装时就会自动覆盖老版本 4.选择程序集 .NET Framework 4.0 5.添加要打包的文件、程序 6.非常重要: 1)如果你的是.NET项目程序DLL、EXE,那就按照默认的设置,不要去改,否则出错 2)如果你的是OCX或者ActiveX等需要注册的DLL,那么选择“Self-registration” 7.设置快捷方式 8.注册表配置: 1)打开这个 2)现在这个测试项目,不需要写注册表信息,所以我什么都不改。如果你的项目要写注册表,那就自己填。 9.安装访谈(安装界面对话框配置): 1)打开这个 2)我就不修改了,大家根据自己的需求调整。 3)如果要修改,点击这个 10.修改安装步骤、对话框、背景等 11.到这这一步,就剩下最重要的问题:如何把.NET Framework一起打包进程序去: 1)解决方案,点击“Specify Application Data”–双击“Redistributables”–勾选“Microsoft .NET Framework 4.0 Full”–勾选之后,它会自动联网下载,下载速度是比较慢的,下完之后,右侧就会变成“Installed Locally” A).NET 4.0:如果大家嫌慢,那就去网上下载:dotNetFx40_Full_x86_x64.exe,然后放到这个路径: C:\Program Files (x86)\InstallShield\2013LE\SetupPrerequisites\Microsoft .net\4.0\Full 这样的话就能节省很多时间了,不过除了这个,它还需要下载其他东西的,只是你可以节省这部分时间 B).NET 3.5 SP1:下载dotnetfx35.exe,然后放到这个路径: C:\Program Files (x86)\InstallShield\2013LE\SetupPrerequisites\Microsoft .net\3.5 SP1\Full 12.最后的设置: 1)解决方案,点击“Prepare for Release”–双击“Releases”–点击选中“SingleImage”–选项卡点击“Setup.exe”–找到 “InstallShield Prerequisites Location”,把它设置为“Extract From Setup.exe” 13.打包完成了,运行效果如上图: 1)打包后的程序放在这个地方:XXX\Express\SingleImage\DiskImages\DISK1\setup.exe 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/8116819.html,如需转载请自行联系原作者
Content-type 的说明 $mimetypes = array( 'ez' => 'application/andrew-inset', 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', 'doc' => 'application/msword', 'bin' => 'application/octet-stream', 'dms' => 'application/octet-stream', 'lha' => 'application/octet-stream', 'lzh' => 'application/octet-stream', 'exe' => 'application/octet-stream', 'class' => 'application/octet-stream', 'so' => 'application/octet-stream', 'dll' => 'application/octet-stream', 'oda' => 'application/oda', 'pdf' => 'application/pdf', 'ai' => 'application/postscript', 'eps' => 'application/postscript', 'ps' => 'application/postscript', 'smi' => 'application/smil', 'smil' => 'application/smil', 'mif' => 'application/vnd.mif', 'xls' => 'application/vnd.ms-excel', 'ppt' => 'application/vnd.ms-powerpoint', 'wbxml' => 'application/vnd.wap.wbxml', 'wmlc' => 'application/vnd.wap.wmlc', 'wmlsc' => 'application/vnd.wap.wmlscriptc', 'bcpio' => 'application/x-bcpio', 'vcd' => 'application/x-cdlink', 'pgn' => 'application/x-chess-pgn', 'cpio' => 'application/x-cpio', 'csh' => 'application/x-csh', 'dcr' => 'application/x-director', 'dir' => 'application/x-director', 'dxr' => 'application/x-director', 'dvi' => 'application/x-dvi', 'spl' => 'application/x-futuresplash', 'gtar' => 'application/x-gtar', 'hdf' => 'application/x-hdf', 'js' => 'application/x-javascript', 'skp' => 'application/x-koan', 'skd' => 'application/x-koan', 'skt' => 'application/x-koan', 'skm' => 'application/x-koan', 'latex' => 'application/x-latex', 'nc' => 'application/x-netcdf', 'cdf' => 'application/x-netcdf', 'sh' => 'application/x-sh', 'shar' => 'application/x-shar', 'swf' => 'application/x-shockwave-flash', 'sit' => 'application/x-stuffit', 'sv4cpio' => 'application/x-sv4cpio', 'sv4crc' => 'application/x-sv4crc', 'tar' => 'application/x-tar', 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', 'texinfo' => 'application/x-texinfo', 'texi' => 'application/x-texinfo', 't' => 'application/x-troff', 'tr' => 'application/x-troff', 'roff' => 'application/x-troff', 'man' => 'application/x-troff-man', 'me' => 'application/x-troff-me', 'ms' => 'application/x-troff-ms', 'ustar' => 'application/x-ustar', 'src' => 'application/x-wais-source', 'xhtml' => 'application/xhtml+xml', 'xht' => 'application/xhtml+xml', 'zip' => 'application/zip', 'au' => 'audio/basic', 'snd' => 'audio/basic', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'kar' => 'audio/midi', 'mpga' => 'audio/mpeg', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'aif' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 'm3u' => 'audio/x-mpegurl', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', 'rpm' => 'audio/x-pn-realaudio-plugin', 'ra' => 'audio/x-realaudio', 'wav' => 'audio/x-wav', 'pdb' => 'chemical/x-pdb', 'xyz' => 'chemical/x-xyz', 'bmp' => 'image/bmp', 'gif' => 'image/gif', 'ief' => 'image/ief', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpe' => 'image/jpeg', 'png' => 'image/png', 'tiff' => 'image/tiff', 'tif' => 'image/tiff', 'djvu' => 'image/vnd.djvu', 'djv' => 'image/vnd.djvu', 'wbmp' => 'image/vnd.wap.wbmp', 'ras' => 'image/x-cmu-raster', 'pnm' => 'image/x-portable-anymap', 'pbm' => 'image/x-portable-bitmap', 'pgm' => 'image/x-portable-graymap', 'ppm' => 'image/x-portable-pixmap', 'rgb' => 'image/x-rgb', 'xbm' => 'image/x-xbitmap', 'xpm' => 'image/x-xpixmap', 'xwd' => 'image/x-xwindowdump', 'igs' => 'model/iges', 'iges' => 'model/iges', 'msh' => 'model/mesh', 'mesh' => 'model/mesh', 'silo' => 'model/mesh', 'wrl' => 'model/vrml', 'vrml' => 'model/vrml', 'css' => 'text/css', 'html' => 'text/html', 'htm' => 'text/html', 'asc' => 'text/plain', 'txt' => 'text/plain', 'rtx' => 'text/richtext', 'rtf' => 'text/rtf', 'sgml' => 'text/sgml', 'sgm' => 'text/sgml', 'tsv' => 'text/tab-separated-values', 'wml' => 'text/vnd.wap.wml', 'wmls' => 'text/vnd.wap.wmlscript', 'etx' => 'text/x-setext', 'xsl' => 'text/xml', 'xml' => 'text/xml', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mpe' => 'video/mpeg', 'qt' => 'video/quicktime', 'mov' => 'video/quicktime', 'mxu' => 'video/vnd.mpegurl', 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie', 'ice' => 'x-conference/x-cooltalk', ); The following table documents the HTTP MIME types that are available when working with Office 2007 documents: Ext MIME Type .doc application/msword .dot application/msword .docx application/vnd.openxmlformats-officedocument.wordprocessingml.document .dotx application/vnd.openxmlformats-officedocument.wordprocessingml.template .docm application/vnd.ms-word.document.macroEnabled.12 .dotm application/vnd.ms-word.template.macroEnabled.12 .xls application/vnd.ms-excel .xlt application/vnd.ms-excel .xla application/vnd.ms-excel .xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet .xltx application/vnd.openxmlformats-officedocument.spreadsheetml.template .xlsm application/vnd.ms-excel.sheet.macroEnabled.12 .xltm application/vnd.ms-excel.template.macroEnabled.12 .xlam application/vnd.ms-excel.addin.macroEnabled.12 .xlsb application/vnd.ms-excel.sheet.binary.macroEnabled.12 .ppt application/vnd.ms-powerpoint .pot application/vnd.ms-powerpoint .pps application/vnd.ms-powerpoint .ppa application/vnd.ms-powerpoint .pptx application/vnd.openxmlformats-officedocument.presentationml.presentation .potx application/vnd.openxmlformats-officedocument.presentationml.template .ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow .ppam application/vnd.ms-powerpoint.addin.macroEnabled.12 .pptm application/vnd.ms-powerpoint.presentation.macroEnabled.12 .potm application/vnd.ms-powerpoint.presentation.macroEnabled.12 .ppsm application/vnd.ms-powerpoint.slideshow.macroEnabled.12 本文转自生鱼片博客园博客,原文链接:http://www.cnblogs.com/carysun/archive/2012/02/01/2335077.html,如需转载请自行联系原作者