至今用过的最好的图片转cad图的软件

R2V 5.5.040330 汉化版下载:R2V.exe

360会提示有捆绑软件,安装的时候要注意去掉。
说明书1

说明书2

说明书3

说明书4

发表在 CAD实用软件 | 留下评论

CAD外挂 链轮 插件 内附说明

选择“工具”—加载应用程序。。。加载后输入awb

下载:LianLun.zip

发表在 CAD实用软件 | 留下评论

CAD图章工具(LSP)

将程序加载到CAD程序里面,运行“tzz”。
1.解压;

2.工具—加载应用程序—选择文件类型—点文件名—加载—启动组—添加;

3.命令行输入tzz,回车健即可应用。

下载:tzz.zip

发表在 CAD实用软件 | 留下评论

autocad2014注册机

32位注册机下载:xf-adsk32.exe

64位注册机下载:xf-adsk64.exe

注意:Win7及以上版本的操作系统必须右键以管理员身份运行才可以。

cad2014注册机

autocad2014注册机 使用方法:


1、安装Autodesk AutoCAD 2014
2、使用这些系列号666-69696969、667-98989898、400-45454545、066-66666666等等以及其它你能找到的能用的系列号。
3、产品密码为001F1
4、安装完成后,重启Autodesk产品
5、在激活前,需要这样做:(二选一)
(1)断开网络(可以拔出网线或用防火墙断开),这一步只是为了避免在线检查。这时会告诉你需要联网,点击关闭并重新点击激活。
(2)点击激活,此时会在线检查,立即点击关闭,并重新点击激活。
6、选择“我已经从Autodesk取得激活码”。
7、在激活的界面上,启动XFORCE Keygen 32bits 或 64bits 版本。
8、点击注册机界面上的Patch,此时会看到successfully patched;
9、复制request code到注册机并点击generate;
10、现在就可以复制激活码到AutoCAD的激活界面,并点击下一步。
这样就完成了Autodesk产品的注册了。

发表在 CAD实用软件 | 标签为 , | 一条评论

cad2016注册机64

xf-adsk2016_x64.exe

网上找的cad2016注册机64大多数不能用或者有木马。

经过多番辛苦查找,总算找到它了。

和所有的CAD注册机一样

必须右键以管理员身份运行

CAD2016注册机

破解步骤

1、安装AUTOCAD,使用序列号666-69696969,400-12345678密钥为001H1。

2、点击注册机”补丁”按钮

3、启动AutoCAD软件,进到离线激活界面。(注意进到离线激活的界面的方法:第一种是断开网络(可以拔出网线或用防火墙断开),这一步只是为了避免在线检查。这时会告诉你需要联网,点击关闭并重新点击激活。第二种是点击激活,此时会在线检查,立即点击关闭,并重新点击激活,选择“我已经从Autodesk取得激活码”)

4、打开注册机(WIN7、WIN8系统要用鼠标右击选‘以管理员身份运行)。

5、复制请求码(request code)到注册机并点击generate(生成)。

6、把激活码复制后粘到激活对话框中,点“下一步”。

7、激活成功!

发表在 CAD实用软件 | 标签为 | 一条评论

XlsToDwg

XlsToDwg_V0.1.7z

Excel文件直接转CAD的dwg文件

不依赖Excel,也不依赖CAD,速度非常快

完全免费

主界面

XlsToDwg

原始Excel文件

XlsToDwg

结果dwg文件

发表在 CAD实用软件 | 标签为 , | 留下评论

OpenDwg开发实例XlsToDwg

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Teigha.TD;
using Teigha.Core;
using Aspose.Cells;
namespace XlsToDwg
{
public class XlsToDwgServer
{
OdDbHostAppServices _hostApp;
OdRxSystemServices _sysSrv;
private XlsToDwgServer()
{
_sysSrv = new ExSystemServices();
_hostApp = new CustomServices();
TD_Db.odInitialize(_sysSrv);
}
private static XlsToDwgServer _Instance = new XlsToDwgServer();
/// <summary>
/// 唯一实例
/// </summary>
public static XlsToDwgServer Instance
{
get
{
return _Instance;
}
}
private void AddIngore(List<KeyValuePair<uint, uint>> ingoreCells, Range range)
{
for (uint i = 0; i < range.RowCount; i++)
{
for (uint j = 0; j < range.ColumnCount; j++)
{
ingoreCells.Add(new KeyValuePair<uint, uint>((uint)(i + range.FirstRow), (uint)(j + range.FirstColumn)));
}
}
}
private bool IsIngore(List<KeyValuePair<uint, uint>> ingoreCells, uint i, uint j)
{
foreach (KeyValuePair<uint, uint> kvp in ingoreCells)
{
if (kvp.Key == i && kvp.Value == j)
{
return true;
}
}
return false;
}
/// <summary>
/// 英寸转毫米
/// </summary>
/// <param name=”inch”></param>
/// <returns></returns>
private double InchToMM(double inch)
{
return inch * 25.4;
}
private void SetTextAlignment(OdDbTable table,uint row,uint column,Cell cell)
{

}
private void TransToTable(OdDbTable table, Cells cells)
{
table.setNumRows((uint)cells.MaxDataRow + 1);
table.setNumColumns((uint)cells.MaxDataColumn + 1);
for (uint i = 0; i <= cells.MaxDataRow; i++)
{
table.setRowHeight(i, InchToMM(cells.GetRowHeightInch((int)i)));
}
for (uint j = 0; j <= cells.MaxDataColumn; j++)
{
table.setColumnWidth(j, InchToMM(cells.GetColumnWidthInch((int)j)));
}
List<KeyValuePair<uint, uint>> ingoreCells = new List<KeyValuePair<uint, uint>>();
for (uint i = 0; i <= cells.MaxDataRow; i++)
{
for (uint j = 0; j <= cells.MaxDataColumn; j++)
{
Cell cell = cells.GetCell((int)i, (int)j);
if (IsIngore(ingoreCells, i, j))
{
continue;
}
if (cell.IsMerged)
{
Range range = cell.GetMergedRange();
table.mergeCells(i, (uint)Math.Min(i + range.RowCount – 1, cells.MaxDataRow), j, (uint)Math.Min(j + range.ColumnCount – 1, cells.MaxDataColumn));
AddIngore(ingoreCells, range);
}
if (cell.Value == null)
continue;
string text = cell.Value.ToString();
Encoding srcEncoding = Encoding.Unicode;
Encoding targetEncoding = Encoding.UTF8;
text = targetEncoding.GetString(Encoding.Convert(srcEncoding, targetEncoding, srcEncoding.GetBytes(text)));
table.setTextString(i, j, text);
table.setTextHeight(i, j, cell.GetStyle().Font.Size / 3.78 / 1.5);
TextAlignmentType ha = cell.GetStyle().HorizontalAlignment;
TextAlignmentType va = cell.GetStyle().VerticalAlignment;
if (ha == TextAlignmentType.Left)
{

if (va == TextAlignmentType.Bottom)
{
table.setAlignment(i, j, CellAlignment.kBottomLeft);
}
else if (va == TextAlignmentType.Center)
{
table.setAlignment(i, j, CellAlignment.kMiddleLeft);
}
else if (va == TextAlignmentType.Top)
{
table.setAlignment(i, j, CellAlignment.kTopLeft);
}
}
else if (ha == TextAlignmentType.Center)
{
if (va == TextAlignmentType.Bottom)
{
table.setAlignment(i, j, CellAlignment.kBottomCenter);
}
else if (va == TextAlignmentType.Center)
{
table.setAlignment(i, j, CellAlignment.kMiddleCenter);
}
else if (va == TextAlignmentType.Top)
{
table.setAlignment(i, j, CellAlignment.kTopCenter);
}
}
else if (ha == TextAlignmentType.Right)
{
if (va == TextAlignmentType.Bottom)
{
table.setAlignment(i, j, CellAlignment.kBottomRight);
}
else if (va == TextAlignmentType.Center)
{
table.setAlignment(i, j, CellAlignment.kMiddleRight);
}
else if (va == TextAlignmentType.Top)
{
table.setAlignment(i, j, CellAlignment.kTopRight);
}
}
}
}
}
OdDbObjectId addStyle(OdDbDatabase pDb,
string styleName)
{
OdDbObjectId styleId;

OdDbTextStyleTable pStyles = (OdDbTextStyleTable)pDb.getTextStyleTableId().safeOpenObject(OpenMode.kForWrite);
OdDbTextStyleTableRecord pStyle = OdDbTextStyleTableRecord.createObject();
pStyle.setName(styleName);

// Add the object to the table.
styleId = pStyles.add(pStyle);
pStyle.setFileName(“txt”);
pStyle.setBigFontFileName(“gbcbig”);

return styleId;
}
public void Trans(string xlsFile,string dwgFile)
{
OdDbDatabase odb = _hostApp.createDatabase(true);
OdDbObjectId styleId=addStyle(odb, “Font_XlsToDwg”);
OdDbDictionary tableDict = (OdDbDictionary)odb.getTableStyleDictionaryId().safeOpenObject(OpenMode.kForWrite);
OdDbTableStyle style=OdDbTableStyle.createObject();
style.setTextStyle(styleId);
OdDbObjectId tableStyleId=tableDict.setAt(“Style_XlsToDwg”, style);
Workbook book = new Workbook(xlsFile);
double xStart = 0F;
for (int i = 0; i < book.Worksheets.Count; i++)
{
Worksheet sheet = book.Worksheets[i];
Cells cells = sheet.Cells;
OdDbTable table = OdDbTable.createObject();
table.setTableStyle(tableStyleId);
TransToTable(table, cells);
OdDbBlockTableRecord pSpace = (OdDbBlockTableRecord)odb.getModelSpaceId().safeOpenObject(OpenMode.kForWrite);
table.setPosition(new OdGePoint3d(xStart, 0, 0));
table.generateLayout();
pSpace.appendOdDbEntity(table);
double dWidth = table.width();
xStart += dWidth * 1.1;
}
odb.writeFile(dwgFile, Teigha.TD.SaveType.kDwg, DwgVersion.kDHL_1800);
}
}
}

发表在 ObjectArx.Net | 标签为 , | 留下评论

FileDialog的Filter

OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = “Microsoft Excel Files(*.xls;*.xlsx)|*.xls;*.xlsx|Microsoft Word Files(*.doc;*.docx)|*.doc;*.docx”;
DialogResult dr=dialog.ShowDialog(this);

Microsoft Excel Files(*.xls;*.xlsx) 显示给用户看的

*.xls;*.xlsx过滤器,两部分构成了一组过滤,表示选择后缀名为xls和xlsx对应的文件

Microsoft Word Files(*.doc;*.docx) 显示给用户看的

*.doc;*.docx 过滤器两部分构成了一组过滤,表示选择后缀名为doc和docx对应的文件

这样构成了两组过滤器供用户选择。

 

发表在 C# | 标签为 , | 留下评论

DsoFile一个查看修改windows文件摘要的好东东

DsoFile一个查看修改windows文件摘要的好东东

同时可以操作自定义属性

基于COM,所有开发语言都可以使用

asp中使用

<%
 Set OPR = Server.CreateObject(“DSOFile.OleDocumentProperties”)
 OPR.open(“E:/PaiMei_demo_001.jpg”)
 Response.Write(“Author:” & OPR.SummaryProperties.Author)
 OPR.SummaryProperties.Author = “0123456789”
 OPR.Save
 response.Write(“<br />”)
 Response.Write(“Author:” & OPR.SummaryProperties.Author)
%>

c#中使用

DSOFile.OleDocumentPropertiesClass dsoFile = new DSOFile.OleDocumentPropertiesClass();
            dsoFile.Open(filename, false, DSOFile.dsoFileOpenOptions.dsoOptionDefault);

dsoFile.Save();
            dsoFile.Close(false);

什么是 Dsofile?

Dsofile 实际上是 Microsoft Developer Support OLE File Property Reader 2.0 Sample 的文件名 (Dsofile.dll)。(您必须承认,Microsoft 自有诀窍想出十分诡异的名称。)不过,不要被这个名称吓倒哦。实际上,Dsofile(您可以从 Microsoft 下载中心免费获取它)是一种对脚本编写者极其有用的工具:它为读取和写入您的所有文件的摘要信息属性集提供了一种快速而简便的方法。

微软只提供了32位版本下载,这里为方便使用编译了一个64位版本,微软提供源码下载

反正编译也是分分钟的事情。

DsoFile.zip

 

发表在 C++ | 标签为 , , , , | 留下评论

返回404的网页为什么百度不做死链处理?

返回404的网页为什么百度不做死链处理?

http://www.cadgj.com/GoodAssistant

这是以前建立的一个论坛,百度收录了一些内容

后面见论坛没什么人气也就停掉了,然后删除了。

差不多有半年了,可是百度还没有将这些内容做死链处理,并删除。

难道真的要一个个去提交才行吗?

百度能不能不这么笨嘛?!

发表在 网站建设 | 标签为 , , | 留下评论

CAD2008注册机

AutoCAD-2008-keygen.zip

CAD2008注册机

为了避免安装时出现未知错误,请在安装注册前断开网络。切记切记!!


AutoCAD2008注册机安装注册说明

1、首先下载AutoCAD2008简体中文版;

2、下面开始安装,首先对下载的AutoCAD2008压缩文件杀毒以防万一,然后用WinRAR或UltraISO工具解压将下载的2个ISO文件到硬盘,最后解压出两个文件夹,CD1文件夹,CD2文件夹;

3、复制CD2文件中的所有文件,粘贴覆盖到CD1文件夹;

4、点击CD1文件夹中的安装文件 “Setup.exe”开始安装。如果点“Setup.exe”不能安装,提示错误,估计是你的电脑操作系统没有安装Microsoft.NETFramework或版本过低,先安装Microsoft.NETFramework,然后再点“Setup.exe”安装;

(正确的安装方法是点击“Setup.exe”安装,而不是点“acad.msi”安装的。)

注意:为了避免安装时出现未知错误,请在安装注册前断开网络。切记切记!!

5、点“安装产品”,按提示选择,或一直点下一步(默认),安装完成后,还需要激活产品;

6、运行AutoCAD2008。注意:一定要先运行AutoCAD2008等出现激活窗口时才打开注册机,否则无法打开注册机运行;

7、出现“激活”页面,提示输入“序列号”和“激活码”。注意:“申请号”和“激活码”复制和粘贴的时候要用快捷键“Ctrl+C”和“Ctrl+V”才行;

运行AutoCAD2008注册机,计算激活码,将“激活”页面中的“申请号”复制在注册机中,点击“Calculate”按钮计算激活码,将在注册机中获得的激活码复制粘贴在激活窗口中激活;

可能会提示无效或提示有啥代码,继续点“Caculate”可以获得不同的激活码;多试几次。还不行再删除C:\Documents and Settings\All Users\Application Data\Autodesk\Software Licenses目录下的*.dat文件,然后重新使用注册机计算激活码,激活产品。当提示AutoCAD 2008激活错误后再删除一次*.dat文件再重新运行注册即可成功。激活成功,(别忘了点完成哦)运行AutoCAD2008。

发表在 CAD实用软件 | 标签为 | 一条评论

cad2012注册机(支持32位和64位)

CAD2012zcj.zip

CAD2012注册机下载安装后,按照下面的教程注册即可。

支持32位和64位只是注册机不一样而已。

下载的压缩包里都有

CAD2012注册机

1. 安装后首次启动 Autodesk AutoCAD 2012

2. 输入序列号:666-69696969 或 667-98989898 或 400-45454545或者400-12345678(貌似从2006~2014都可以用的序列号),输入密匙:001D1;

CAD2012注册机

3. 完成安装,重启CAD;

 

 

 4. 点击激活按钮之前你有2个选择:
a)禁用您的网络或拔掉网线;
b)点击激活后它会告诉您,您的序列号是错误的,这时点击“上一步”等一会再点击“激活”即可。
选择了a或b后看下一步。
5. 在激活界面中选择“我拥有一个Autodesk激活码”;
6. 一旦到了激活界面,启动注册机(记住如果不在激活界面的话注册机是无效的,必须在激活界面才能使用)。如果你是32位的请启动32位的注册机,
如果是64位的请启动64位的注册机(如果使用的是Win7系统选择以管理员身份运行注册机);
7. 先粘贴激活界面的申请号至注册机中的 Request 栏中;
8. 点击 Generate 算出激活码,并点击 Mem Patch 键,否则无法激活;
9. 最后复制 Activation 中的激活码至“输入激活码”栏中,并点击“下一步”,
你已拥有一个完全注册 Autodesk 产品。

CAD2010注册机

发表在 CAD实用软件 | 标签为 , , | 留下评论

终止正在执行的线程

很多人都说使用Abort方法来终止线程,其实这种做法并不可取!如果你的线程正在操作临界资源,很有可能会造成资源没有正确释放而出现死锁问题。正确的做法应该是使用标记来终止线程的执行。
基本思路是:定义一个用于描述“停止”信号的变量,在整个程序启动前,设置该变量为false。在线程中,循环判断该变量是否已经被设置为true,如果没有,则继续执行,否则就退出循环并释放资源,然后退出执行。当我们需要线程退出时,只要设置这个“停止”信号为true即可。
 
下面我们来看具体的操作步骤。
首先定义一个“停止”信号变量:
view plaincopy to clipboardprint?
  1. private volatile bool canStop = false;  
注意这里我们使用了volatile关键字,因为canStop变量将会被调用线程和执行线程同时使用,即在调用线程中初始化以及设置它的值,而在执行线程中判断它的值。这样做就告诉编译器,这个canStop变量将被多个线程所使用,强制编译器不对其状态进行优化。如果有兴趣可以上MSDN查看关于这个volatile关键字的更多解释。此处同时也给canStop做了初始化。
 
现在我们看看线程的创建与执行的代码:
 
view plaincopy to clipboardprint?
  1. i = 0;   
  2. // 使用匿名方法定义线程的执行体   
  3. Thread thread = new Thread(   
  4.     delegate(object param)   
  5.     {   
  6.         // 等待“停止”信号,如果没有收到信号则执行   
  7.         while (!canStop)   
  8.         {   
  9.             i++;   
  10.             UpdateLabel(i);   
  11.         }   
  12.         // 此时已经收到停止信号,可以在此释放资源并   
  13.         // 初始化变量   
  14.         canStop = false;   
  15.     });   
  16.   
  17. thread.Start();  
 很简单,在线程的执行体中反复判断canStop变量是否为true,若是则立刻跳出while循环(停止变量的自加以及更新界面的操作),然后重新初始化canStop变量为false,以便于下次的使用。
 
这里提供源代码下载,请下载源代码工程并在Visual Studio 2008下打开*.sln解决方案文件。

 

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/06/1687017.html

发表在 C# | 留下评论

改变应用程序配置文件的文件名

应用程序配置文件的文件名是可以任意更改的。比如:

  1. 向当前的可执行项目中添加一个XML文件,取名abc.config,输入以下代码
     

    view plaincopy to clipboardprint?
    1. <?xml version=“1.0” encoding=“utf-8” ?>  
    2. <configuration>  
    3.   <appSettings>  
    4.     <add key=“k” value=“v”/>  
    5.   </appSettings>  
    6. </configuration>  
    7.  
  2. 设置abc.config文件的Copy To Output Directory属性为Copy always
  3. 可以使用下面的代码来读取abc.config文件中的配置信息,就好像读取App.config那样
     

    1. static void Main(string[] args)   
    2. {   
    3.     ExeConfigurationFileMap map = 
    4.         new ExeConfigurationFileMap();   
    5.     
    6.     map.ExeConfigFilename = “abc.config”;   
    7.     
    8.     Configuration config = 
    9.       ConfigurationManager.OpenMappedExeConfiguration(
    10.         map, ConfigurationUserLevel.None);   
    11.     
    12.     string s = config.AppSettings.Settings[“k”].Value;   
    13. }   
    14.  

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/06/1687016.html

发表在 C# | 留下评论

C#基础:值类型、引用类型与ref关键字

在C#中,ref的意思是按引用传递。可以参考C++:

view plaincopy to clipboardprint?
  1. int a = 10, b = 20;   
  2. void swap(int x, int y)   
  3. {   
  4.     int temp = x;   
  5.     x = y;   
  6.     y = temp;   
  7. }  

如果简单的调用这个swap,比如:swap(a, b),那么你根本没办法交换这两个变量的值,因为x和y都是形参,在swap返回的时候,x和y都被释放了。但如果是这样定义swap:

view plaincopy to clipboardprint?
  1. void swap (int& x, int& y)   
  2. {   
  3.     int temp = x;   
  4.     x = y;   
  5.     y = temp;   
  6. }  

 

也就相当于x与a,y与b指向同一个内存地址,那么对x的操作也就相当于对a的操作。那么在C#里面,这种效果对于值类型是很明显的。

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static void Test(ref int b)   
  4.     {   
  5.         b = 2;   
  6.     }   
  7.   
  8.     static void Main(string[] args)   
  9.     {   
  10.         int b = 1;   
  11.         Test(ref b);   
  12.         Console.WriteLine(b);   
  13.     }   
  14. }   

此时的输出是2,也就是Test方法中的b与Main中的b指向同一个内存地址,那么对Test.b的操作也就是对Main.b的操作。你如果把程序改成:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static void Test(int b)   
  4.     {   
  5.         b = 2;   
  6.     }   
  7.   
  8.     static void Main(string[] args)   
  9.     {   
  10.         int b = 1;   
  11.         Test(b);   
  12.         Console.WriteLine(b);   
  13.     }   
  14. }   

 

那么输出的还是1,因为Test.b不是Main.b的引用,也就是一个单独的形参。现在再看对于引用类型,会是什么效果:

view plaincopy to clipboardprint?
  1. class TestClass   
  2. {   
  3.     public int b;   
  4. }   
  5.   
  6. class Program   
  7. {   
  8.     static void Test(TestClass b)   
  9.     {   
  10.         b.b = 2;   
  11.     }   
  12.   
  13.     static void Main(string[] args)   
  14.     {   
  15.         TestClass b = new TestClass();   
  16.         b.b = 1;   
  17.         Test(b);   
  18.         Console.WriteLine(b.b);   
  19.     }   
  20. }   

上面的代码,输出的是2,因为b是引用类型,在只需修改b的成员的时候,加不加ref关键字都一样。引用类型本身并不包含数据,仅仅维持了对数据的引用。

因此,使用ref参数,对值类型对象的作用显而易见,而对于引用类型,如需修改引用类型内部的数据,则无需使用ref关键字;否则,当被调用函数内部需要更改引用本身时,比如在函数内部重新定位对象的引用,则需要使用ref关键字。

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/07/1687015.html

发表在 C# | 留下评论

C#基础:委托

委托是C#中最为常见的内容。与类、枚举、结构、接口一样,委托也是一种类型。类是对象的抽象,而委托则可以看成是函数的抽象。一个委托代表了具有相同参数列表和返回值的所有函数。比如:

view plaincopy to clipboardprint?
  1. delegate int GetCalculatedValueDelegate(int x, int y);  

在上面的定义中,我们定义了一个委托,这个委托代表着一类函数,这些函数的第一个参数是整数型的x,第二个参数是整数型的y,而函数的返回值则是一个整数。在这里,为了描述方便,我们把这一类的函数称为具有相同签名(signature)的函数(注意:这个签名并不是数字签名中的概念,而只是表示这类函数具有相同的参数列表和返回值)。

既然委托是一种类型,那么它就能被用来定义参数、变量以及返回值。由委托定义的变量用于保存具有相同签名的函数实体。需要注意的是,C#和C++不同,C++中的函数指针只能保存全局的或者静态的函数,而C#中的委托实体则可以指代任何函数。

现在我们来看一个例子,在这个例子中,我们使用了上面定义的那个委托,并创建了一个委托实体,使其指代程序中的AddCalculator函数,接下来就可以直接像使用函数本身一样,使用这个委托实体来获得计算的结果。

view plaincopy to clipboardprint?
  1. delegate int GetCalculatedValueDelegate(int x, int y);   
  2.   
  3. static int AddCalculator(int x, int y)   
  4. {   
  5.     return x + y;   
  6. }   
  7.   
  8. static int SubCalculator(int x, int y)   
  9. {   
  10.     return x – y;   
  11. }   
  12.   
  13. static void Main(string[] args)   
  14. {   
  15.     GetCalculatedValueDelegate d = AddCalculator;   
  16.     Console.WriteLine(d(10, 20));   
  17. }   

到这里也就能基本上明白“委托”的意义了,针对上面的Main函数,本来需要调用AddCalculator函数的,却通过d来调用了,也就是,后续对AddCalculator的操作由d代为效劳。本来是要小明去老师办公室拿粉笔盒的,由于小明和小文是好朋友,因此小明就要小文代他去拿,于是小文成了小明的代理,小明委托小文去拿粉笔盒。

现在我们来考虑委托作为参数的情形。将委托作为参数,可以把函数本身的处理逻辑抽象出来,而让调用者决定最终使用什么样的逻辑去处理。请看下面的例子:

view plaincopy to clipboardprint?
  1. delegate int GetCalculatedValueDelegate(int x, int y);   
  2.   
  3. static int AddCalculator(int x, int y)   
  4. {   
  5.     return x + y;   
  6. }   
  7.   
  8. static int SubCalculator(int x, int y)   
  9. {   
  10.     return x – y;   
  11. }   
  12.   
  13. static int Calculator(GetCalculatedValueDelegate del, int x, int y)   
  14. {   
  15.     return del(x, y);   
  16. }   
  17.   
  18. static void Main(string[] args)   
  19. {   
  20.     Console.WriteLine(Calculator(AddCalculator, 10, 20));   
  21. }   

在上面的例子中,Calculator函数的第一个参数就是一个委托。事实上,Calculator对x和y将会做什么处理,它本身并不知道,如何处理x和y由GetCalculatedValueDelegate来决定。那么在Main方法里,我们将AddCalculator方法作为参数传递给Calculator,表示让Calculator用AddCalculator的逻辑去处理x和y。这也很形象:Calculator说:“我不知道要怎么处理x和y,让del去处理好了!”于是就把x和y扔给了del。

这种做法其实跟“模板方法模式”有点点类似。在模板方法模式中,可以将可变的部分留给子类去重写,而将不变的部分由父类实现。那么在委托作为参数的情况下,Calculator可以自己处理不变的逻辑,而将“具体怎么做”的事情委托给他人去办理。

委托作为参数,在C#中非常常见。比如线程的创建,需要给一个ThreadStart或者ParameterizedThreadStart委托作为参数,而在线程执行的时候,将这个参数所指代的函数用作线程执行体。再比如:List<T>类型的Find方法的参数也是一个委托,它把“怎么去查找”或者说“怎么样才算找到”这个问题留给了开发人员。开发人员只需要定义一个参数为T,返回值为布尔型的函数,实现函数体,并将函数作为参数传给Find方法,就可以完成集合中元素的查找。

委托作为返回值一般会用在“根据不同情况决定使用不同的委托”这样的情形下。这有点像工厂模式,不过委托用作返回值还是用的没有用作参数这样频繁。

与委托相关的概念还有很多,比如异步调用、泛型委托、匿名方法、Lambda表达式、事件、协变与逆变等。我会在后续的文章中陆续介绍。

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/08/1687014.html

发表在 C# | 留下评论

C#基础:异步调用

首先来看一个简单的例子:

  1. 小明在烧水,等水烧开以后,将开水灌入热水瓶,然后开始整理家务
  2. 小文在烧水,在烧水的过程中整理家务,等水烧开以后,放下手中的家务活,将开水灌入热水瓶,然后继续整理家务

这也是日常生活中很常见的情形,小文的办事效率明显要高于小明。从C#程序执行的角度考虑,小明使用的同步处理方式,而小文则使用的异步处理方式。

同步处理方式下,事务是按顺序一件一件处理的;而异步方式则是,将子操作从主操作中分离出来,主操作继续进行,子操作在完成处理的时候通知主操作。

在C#中,异步通过委托来完成。请看下面的例子:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static TimeSpan Boil()   
  4.     {   
  5.         Console.WriteLine(“水壶:开始烧水…”);   
  6.         Thread.Sleep(6000);   
  7.         Console.WriteLine(“水壶:水已经烧开了!”);   
  8.         return TimeSpan.MinValue;   
  9.     }   
  10.   
  11.     delegate TimeSpan BoilingDelegate();   
  12.   
  13.     static void Main(string[] args)   
  14.     {   
  15.         Console.WriteLine(“小文:将水壶放在炉子上”);   
  16.         BoilingDelegate d = new BoilingDelegate(Boil);   
  17.         IAsyncResult result = d.BeginInvoke(BoilingFinishedCallback, null);   
  18.         Console.WriteLine(“小文:开始整理家务…”);   
  19.         for (int i = 0; i < 20; i++)   
  20.         {   
  21.             Console.WriteLine(“小文:整理第{0}项家务…”, i + 1);   
  22.             Thread.Sleep(1000);   
  23.         }   
  24.     }   
  25.   
  26.     static void BoilingFinishedCallback(IAsyncResult result)   
  27.     {   
  28.         AsyncResult asyncResult = (AsyncResult)result;   
  29.         BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;
  30.         del.EndInvoke(result);
  31.         Console.WriteLine(“小文:将热水灌到热水瓶”);   
  32.         Console.WriteLine(“小文:继续整理家务”);   
  33.            
  34.     }   
  35. }  

上面的例子是一个最简单的异步调用的例子,没有对异步调用函数做任何参数传递以及返回值校验。这个例子反映了小文烧水的流程,首先小文将水壶放在炉子上,在定义好委托以后,就使用BeginInvoke方法开始异步调用,即让水壶开始烧水,于是小文便开始整理家务。水烧开后,C#的异步模型会触发由BeginInvoke方法所指定的回调函数,也就是水烧开后的处理逻辑由这个回调函数定义,此时小文将水灌入热水瓶并继续整理家务。

由此可见,在C#中实现异步调用其实并不复杂,首先创建一个异步处理函数,并针对其定义一个委托;然后在调用函数的时候,使用委托的BeginInvoke方法,指定在函数处理完成时的回调函数(如果不需要对完成事件做处理,可以给null值),并指定所需的参数(如果没有参数,也可以给null值);最后在回调函数中处理完成事件。

请注意上例回调函数中的EndInvoke调用,EndInvoke会使得调用线程阻塞,直到异步函数处理完成。显然,紧接在BeginInvoke后面的EndInvoke使用方式与同步调用等价。

 EndInvoke调用的返回值也就是异步处理函数的返回值。我们把程序稍作修改,将Boil方法改成下面的形式:

view plaincopy to clipboardprint?
  1. static TimeSpan Boil()   
  2. {   
  3.     DateTime begin = DateTime.Now;   
  4.     Console.WriteLine(“水壶:开始烧水…”);   
  5.     Thread.Sleep(6000);   
  6.     Console.WriteLine(“水壶:水已经烧开了!”);   
  7.     return DateTime.Now – begin;   
  8. }   

然后将BoilingFinishedCallback改成下面的形式:

  1. static void BoilingFinishedCallback(IAsyncResult result)   
  2. {   
  3.     AsyncResult asyncResult = (AsyncResult)result;   
  4.     BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;   
  5.     Console.WriteLine(“(烧水一共用去{0}时间)”, del.EndInvoke(result));   
  6.     Console.WriteLine(“小文:将热水灌到热水瓶”);   
  7.     Console.WriteLine(“小文:继续整理家务”);   
  8. }   

那么我们就可以在EndInvoke的时候,获得由Boil异步处理函数返回的时间值。事实上,如果定义的BoilingDelegate委托存在参数列表,那么我们也可以在BeginInvoke的时候,将所需的参数传给异步处理函数。BeginInvoke/EndInvoke函数的签名与定义它们的委托签名有关。

注意:在修改后的BoilingFinishedCallback方法中,为了得到委托实例以便获取异步处理函数的返回值,我们采用了下面的转换:

view plaincopy to clipboardprint?
  1. AsyncResult asyncResult = (AsyncResult)result;   
  2. BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;   

这样才能获得调用异步处理函数的委托的实体。

.NET处理异步函数调用,事实上是通过线程来完成的。这个过程有以下几个特点:

  • 异步函数由线程完成,这个线程是.NET线程池中的线程
  • 通常情况下,.NET线程池拥有500个线程(当然这个数量可以设置),每当调用BeginInvoke开始异步处理时,异步处理函数就由线程池中的某个线程负责执行,而用户无法控制具体是由哪个线程负责执行
  • 由于线程池中线程数量有限,因此当池中线程被完全占用时,新的调用请求将使函数不得不等待空余线程的出现。此时,程序的效率会有所影响

为了验证这些特点,请看下面的程序:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     delegate void MethodInvoker();   
  4.   
  5.     static void Foo()   
  6.     {   
  7.         int intAvailableThreads, intAvailableIoAsynThreds;   
  8.   
  9.         ThreadPool.GetAvailableThreads(out intAvailableThreads,   
  10.                 out intAvailableIoAsynThreds);   
  11.   
  12.         string strMessage =   
  13.             String.Format(@”Is Thread Pool: {0},   
  14.         Thread Id: {1} Free Threads {2}”,   
  15.                 Thread.CurrentThread.IsThreadPoolThread.ToString(),   
  16.                 Thread.CurrentThread.GetHashCode(),   
  17.                 intAvailableThreads);   
  18.   
  19.         Console.WriteLine(strMessage);   
  20.   
  21.         Thread.Sleep(10000);   
  22.   
  23.         return;   
  24.     }   
  25.   
  26.     static void CallFoo()   
  27.     {   
  28.         MethodInvoker simpleDelegate =   
  29.             new MethodInvoker(Foo);   
  30.   
  31.         for (int i = 0; i < 15; i++)   
  32.         {   
  33.             simpleDelegate.BeginInvoke(nullnull);   
  34.         }   
  35.     }   
  36.   
  37.     static void Main(string[] args)   
  38.     {   
  39.         ThreadPool.SetMaxThreads(10, 10);   
  40.         CallFoo();   
  41.         Console.ReadLine();   
  42.     }   
  43. }   

这个程序在起始的时候将线程池中最大线程个数设置为10个,然后做15次异步调用,每个异步调用中都停留10秒钟当作处理本身所要消耗的时间。从程序的执行我们可以看到,当前10个异步调用完全开始以后,新的异步调用就会等待(注意:不是主线程在等待),直到线程池中有线程空闲出来。

参考资料:异步方法调用

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/10/1687013.html

发表在 C# | 留下评论

C#基础:泛型委托

泛型委托是委托的一种特殊形式,感觉看上去比较怪异,其实在使用的时候跟委托差不多,不过泛型委托更具有类型通用性。

就拿C#里最常见的委托EventHandler打比方。在.NET 2.0以前,也就是泛型出现以前,普通的事件处理函数都由EventHandler定义,如下:

view plaincopy to clipboardprint?
  1. public delegate void EventHandler(object sender, EventArgs e);  

EventHandler指代了这样一类函数,这些函数没有返回值,并且有两个参数,第一个参数是object类型,而第二个参数是EventArgs类型。

而.NET 2.0及其以后的版本,由于泛型的引入,所以一些内建(Built-in)的类、接口、委托都有了各自的泛型版本。EventHandler也不例外,它有了自己的泛型版本:EventHandler<T>,它的定义如下:

view plaincopy to clipboardprint?
  1. [Serializable]   
  2. public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs;  

您应该可以发现,第二个参数的类型由EventArgs变成了TEventArgs,而TEventArgs具体是什么,则由调用方决定。假设IntEventArgs和StringEventArgs都继承于System.EventArgs,那么:

  • EventHandler<IntEventArgs>指代这样一类函数:这些函数没有返回值,有两个参数,第一个参数是object类型,第二个参数是IntEventArgs类型
  • EventHandler<StringEventArgs>指代这样一类函数:这些函数没有返回值,有两个参数,第一个参数是object类型,第二个参数是StringEventArgs类型

其实EventHandler<IntEventArgs>和EventHandler<StringEventArgs>是两个完全不同的委托,它们所指代的函数都分别有着不同的签名形式。请参见下面的示例:

view plaincopy to clipboardprint?
  1. class IntEventArgs : System.EventArgs   
  2. {   
  3.     public int IntValue { getset; }   
  4.     public IntEventArgs() { }   
  5.     public IntEventArgs(int value)    
  6.     { this.IntValue = value; }   
  7. }   
  8.   
  9. class StringEventArgs : System.EventArgs   
  10. {   
  11.     public string StringValue { getset; }   
  12.     public StringEventArgs() { }   
  13.     public StringEventArgs(string value)    
  14.     { this.StringValue = value; }   
  15. }   
  16.   
  17. class Program   
  18. {   
  19.     static void PrintInt(object sender, IntEventArgs e)   
  20.     {   
  21.         Console.WriteLine(e.IntValue);   
  22.     }   
  23.   
  24.     static void PrintString(object sender, StringEventArgs e)   
  25.     {   
  26.         Console.WriteLine(e.StringValue);   
  27.     }   
  28.   
  29.     static void Main(string[] args)   
  30.     {   
  31.         EventHandler<IntEventArgs> ihandler =    
  32.             new EventHandler<IntEventArgs>(PrintInt);   
  33.         EventHandler<StringEventArgs> shandler =    
  34.             new EventHandler<StringEventArgs>(PrintString);   
  35.   
  36.         ihandler(nullnew IntEventArgs(100));   
  37.         shandler(nullnew StringEventArgs(“Hello World”));   
  38.     }   
  39. }   

有关泛型的具体特性与其在面向对象思想中的应用,将在后续与泛型相关的文章中重点阐述。

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/11/1687012.html

发表在 C# | 留下评论

C#基础:匿名方法

匿名方法是C# 2.0的语言新特性。首先看个最简单的例子:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static void Main(string[] args)   
  4.     {   
  5.         List<string> names = new List<string>();   
  6.         names.Add(“Sunny Chen”);   
  7.         names.Add(“Kitty Wang”);   
  8.         names.Add(“Sunny Crystal”);   
  9.            
  10.         List<string> found = names.FindAll(   
  11.             new Predicate<string>(NameMatches));   
  12.   
  13.         if (found != null)   
  14.         {   
  15.             foreach (string str in found)   
  16.                 Console.WriteLine(str);   
  17.         }   
  18.     }   
  19.   
  20.     static bool NameMatches(string name)   
  21.     {   
  22.         return name.StartsWith(“sunny”,    
  23.             StringComparison.OrdinalIgnoreCase);   
  24.     }   
  25. }   

这段代码在开始的时候初始化了一个字符串列表(string list),然后通过列表的FindAll方法来查找以“sunny”起始的字符串,最后将所查找到的所有结果输出。

我们需要着重介绍List<T>的FindAll方法。这个方法是一个参数为Predicate<T>类型、返回值为List<T>类型的函数。注意,Predicate<T>是一个泛型委托,它指代这样一些函数:这些函数仅有一个T类型的参数,并且返回值是布尔类型。通过reflector等工具,我们可以看到Predicate<T>的定义如下:

view plaincopy to clipboardprint?
  1. public delegate bool Predicate<T>(T obj);   

至此我们也多少能够猜到FindAll方法的具体实现。即针对List<T>中的每个元素,调用Predicate<T>所指代的函数,如果函数返回为true,则将其加入新建的列表中。遍历完所有的元素后,将新建的列表返回给调用者。如下:

view plaincopy to clipboardprint?
  1. public List<T> FindAll<T>(Predicate<T> match)   
  2. {   
  3.     List<T> ret = new List<T>();   
  4.     foreach (T elem in items)   
  5.     {   
  6.         if (match(elem))   
  7.             ret.Add(elem);   
  8.     }   
  9.     return ret;   
  10. }   

因此,针对上面的例子,要调用FindAll方法,我们必须先定义一个参数为string类型,返回值为布尔类型的函数,在这个函数中,对参数进行条件判断,如果符合条件(也就是以“sunny”作为起始字符串),那么就返回true,否则返回false。最后再将这个函数作为参数传递给FindAll。于是也就得到了最上面的代码。

在上面的例子中,为了调用FindAll方法,我们不得不新定义一个函数,其实这个函数除了FindAll方法要用外,别的地方都几乎很少使用到它,你还不得不给它起个名字。如果程序中有多处需要调用FindAll方法,或者类似的情况,那么整个程序也就会出现一大批“只有一个地方使用”的函数,使得代码难于阅读和维护。

由于存在这样的问题,C# 2.0引入了匿名方法。开发人员在实现方法的时候,只需要给出方法的参数列表(甚至也可以不给)以及方法具体实现,而不需要关心方法的返回值,更不必给方法起名字。最关键的是,只在需要的地方定义匿名方法,保证了代码的简洁。

匿名方法只在需要的地方定义,定义的时候,使用delegate关键字,后接参数列表,然后跟上用一对花括号包括起来的函数体即可。上面的代码可以重构成下面的形式:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static void Main(string[] args)   
  4.     {   
  5.         List<string> names = new List<string>();   
  6.         names.Add(“Sunny Chen”);   
  7.         names.Add(“Kitty Wang”);   
  8.         names.Add(“Sunny Crystal”);   
  9.   
  10.         List<string> found = names.FindAll(   
  11.             delegate(string name)   
  12.             {   
  13.                 return name.StartsWith(“sunny”,   
  14.                     StringComparison.OrdinalIgnoreCase);   
  15.             });   
  16.   
  17.         if (found != null)   
  18.         {   
  19.             foreach (string str in found)   
  20.                 Console.WriteLine(str);   
  21.         }   
  22.     }   
  23.   
  24.     //static bool NameMatches(string name)   
  25.     //{   
  26.     //    return name.StartsWith(“sunny”,    
  27.     //        StringComparison.OrdinalIgnoreCase);   
  28.     //}   
  29. }   

此时,我们完全不需要NameMatches方法了,直接将匿名方法作为参数传递给FindAll方法。其实匿名方法本身还是有名字的,只是我们并不关心它究竟该取什么名字,因而.NET帮我们随便取了个名字罢了。

匿名方法在C#中应用十分广泛,因为委托作为函数参数是件非常平常的事情。在定义简单的事件处理过程时,我们同样可以使用匿名方法。比如:

view plaincopy to clipboardprint?
  1. ServiceHost host = new ServiceHost(typeof(FileTransferImpl));   
  2. host.Opened += delegate(object sender, EventArgs e)   
  3. {   
  4.     Console.WriteLine(“Service Opened.”);   
  5. };   

匿名方法可以很方便地使用本地变量,这与单独定义的命名方法相比,能够简化编程。比如上文的例子中,假如Main函数里面定义了一个整型本地变量(局部变量)number,那么可以在delegate (string name)这一匿名方法定义中使用number变量。

上文提到,在定义匿名方法的时候,连参数列表都可以省略。因为编译器可以根据委托的签名来确定函数的签名,然后只要再给函数起个名字就可以了。下面的代码演示了这种使用方式:

view plaincopy to clipboardprint?
  1. delegate void IntDelegate(int x);   
  2.   
  3. // 带参数的定义方式   
  4. IntDelegate d2 = delegate(int p) { Console.WriteLine(p); };   
  5. // 不带参数的定义方式(当然也没带返回值)   
  6. IntDelegate d3 = delegate { Console.WriteLine(“Hello.”); };   

在使用不带参数和返回值的匿名方法定义时,需要注意以下两点:

  1. 如果在你的匿名方法中需要对参数进行处理,那么你不能使用不定义参数列表的声明方式。也就是在定义匿名方法的时候,需要给出参数列表
  2. 不带参数和返回值的匿名方法,可以被具有任何形式签名的委托所指代

上述第一点显而易见,因为你没有定义参数列表,也就没有办法使用参数;要说明第二点,我们可以看下面的代码:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     delegate void IntDelegate(int x);   
  4.     delegate void StringDelegate(string y);   
  5.   
  6.     static void Output(IntDelegate id)   
  7.     {   
  8.     }   
  9.   
  10.     static void Output(StringDelegate sd)   
  11.     {   
  12.     }   
  13.   
  14.     static void Main(string[] args)   
  15.     {   
  16.         /*  
  17.          * ERROR: The call is ambiguous between  
  18.          *        Output(IntDelegate)  
  19.          *              and  
  20.          *        Output(StringDelegate)  
  21.          */  
  22.         Output(delegate { });   
  23.     }   
  24. }   

上面的代码没法编译通过,因为编译器不知道应该将delegate { }这一匿名方法还原为由IntDelegate指代的函数,还是还原为由StringDelegate指代的函数。此时只能显式给定参数列表,以便让编译器知道,我们究竟是想调用哪个Output函数。

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/12/1687011.html

发表在 C# | 留下评论

C#基础:Lambda表达式

从委托的角度来看,Lambda表达式与匿名方法没有区别。在【C#基础:匿名方法】一文中,我使用了匿名方法来调用List<T>的FindAll方法。从C# 3.0开始,在使用匿名方法的地方,完全可以用Lambda表达式来代替。Lambda表达式的定义方式为:“([参数列表]) => 表达式”。运算符“=>”是一种与赋值运算“=”具有相同优先级的右结合运算符,在英语里读作:“goes to”。

现在回过头来看我们的例子。下面的代码与【C#基础:匿名方法】一文中的代码具有相同的效果:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static void Main(string[] args)   
  4.     {   
  5.         List<string> names = new List<string>();   
  6.         names.Add(“Sunny Chen”);   
  7.         names.Add(“Kitty Wang”);   
  8.         names.Add(“Sunny Crystal”);   
  9.   
  10.         List<string> found = names.FindAll   
  11.             (   
  12.             // Lambda Expression Implementation   
  13.             name => name.StartsWith(   
  14.                 “sunny”,    
  15.                 StringComparison.OrdinalIgnoreCase)   
  16.             );   
  17.   
  18.         if (found != null)   
  19.         {   
  20.             foreach (string str in found)   
  21.                 Console.WriteLine(str);   
  22.         }   
  23.     }   
  24. }   

上面的Lambda Expression Implementation在效果上与匿名方法没有任何区别,“=>”左边的name定义了参数(当参数个数为1的时候,圆括号可以省略),“=>”右边定义执行体。由于C# 3.0编译器具有Type Inference的能力,参数类型与返回值都将由编译器通过上下文判定,因此与匿名方法不同,Lambda表达式的参数可以不给定参数类型。当所表示的匿名方法没有任何参数时,Lambda表达式也同样可以使用,只需在“=>”左边用一对圆括号表示即可。即:

view plaincopy to clipboardprint?
  1. () => Console.WriteLine(“Hello!”);   

事实上,“Lambda表达式”这一词比较笼统,事实上“=>”运算符既可以表示Lambda表达式,也可以表示Lambda语句。Lambda语句由代码块组成,形式上很像匿名方法。请看下面的例子:

view plaincopy to clipboardprint?
  1. class Program   
  2. {   
  3.     static void Main(string[] args)   
  4.     {   
  5.         // Lambda 表达式   
  6.         Func<intbool> dele1 = n => n > 10;   
  7.         // Lambda 语句   
  8.         Func<intbool> dele2 = (int n) => { return n > 10; };   
  9.         Console.WriteLine(dele1(16));   
  10.         Console.WriteLine(dele1(8));   
  11.     }   
  12. }   

两种定义方法同样可以正确地输出结果。请注意,当我们希望构建表达式树的时候,情况却完全不同了:

view plaincopy to clipboardprint?
  1. // ok   
  2. Expression<Func<intbool>> expr1 = n => n > 10;   
  3. // error: cannot converted to an expression tree   
  4. Expression<Func<intbool>> expr2 = (int n) => { return n > 10; };   

由此可见,在构建表达式树的时候,不能用Lambda语句(带有代码语句的Lambda表达式),而应该使用Lambda表达式。从这里就可以看出匿名方法与Lambda表达式的区别了。

有关委托的高级话题以及表达式树的内容,我会在后面的文章中继续研究。

转载至:http://www.cnblogs.com/daxnet/archive/2008/11/14/1687010.html

发表在 C# | 留下评论