首页 文章 精选 留言 我的

精选列表

搜索[c ],共10000篇文章
优秀的个人博客,低调大师

C#对接----韵达开发平台--取电子面单

引子 最近根据业务的一些需求,所以放弃从快递鸟对接去电子面单,转而直接对接韵达开发平台:http://open.yundasys.com/ ,中间踩了一些坑,借此做了一个小案例给大伙,瞅瞅,若有需改进之处,还请指出!!! 废话不多数:首先咱先对韵达的一些接口参数了解清楚: 当然附上地址:http://open.yundasys.com/index.php?g=&m=ApiTools&a=exm 还有接口的一些SDK文件地址,这个就各位观众大老爷们自己去看了:http://open.yundasys.com/index.php?g=&m=ApiTools&a=apps&id=14 解决方案 上代码走起:基础参数的模型 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { //请求参数 class RequestVO { /// <summary> /// XML数据内容 /// </summary> public string xmldata { get; set; } /// <summary> /// 合作社区ID,由韵达给大客户提供 /// </summary> public string partnerid { get; set; } /// <summary> /// 密码 /// </summary> public string password { get; set; } /// <summary> /// 数据请求类型,如request=data;其中data表示下单,详细请见request字典表 /// </summary> public string request { get; set; } /// <summary> /// 请求的版本,当前版本为1.0 /// </summary> public string version { get; set; } } } 主体参数模型 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Serialization; namespace ConsoleApplication2 { /// <summary> /// 数据体 /// </summary> public class Orders { [XmlElement("order")] public List<Order> order { get; set; } } /// <summary> /// 韵达取号订单信息 /// </summary> public class Order { /// <summary> /// 订单唯一序列号 /// </summary> public string order_serial_no { get; set; } /// <summary> /// 大客户系统订单的订单号 /// </summary> public string khddh { get; set; } /// <summary> /// 内部参考号,供大客户自己使用,可以是客户的客户编号 /// </summary> public string nbckh { get; set; } /// <summary> /// 单号 /// </summary> public string mailno { get; set; } /// <summary> /// 发件人 /// </summary> [XmlElement("sender")] public Sender sender { get; set; } /// <summary> /// 收件人 /// </summary> [XmlElement("receiver")] public Receiver receiver { get; set; } /// <summary> /// 物品重量 /// </summary> public long weight { get; set; } /// <summary> /// 尺寸,格式(长,宽,高),单位cm /// </summary> public string size { get; set; } /// <summary> /// 货物金额 /// </summary> public decimal value { get; set; } /// <summary> /// 商品集合 /// </summary> [XmlElement("items")] public Items items { get; set; } /// <summary> /// 订单备注 /// </summary> public string remark { get; set; } /// <summary> /// 可以自定义显示信息1 /// </summary> public string cus_area1 { get; set; } /// <summary> /// 可以自定义显示信息2 /// </summary> public string cus_area2 { get; set; } } public class Sender { /// <summary> /// 姓名 /// </summary> public string name { get; set; } /// <summary> /// 公司 /// </summary> public string company { get; set; } /// <summary> /// 严格按照国家行政区划,省市区三级,逗号分隔。示例上海市,上海市,青浦区(cod订单必填) /// </summary> public string city { get; set; } /// <summary> /// 需要将省市区划信息加上,例如:上海市,上海市,青浦区盈港东路7766号 /// </summary> public string address { get; set; } /// <summary> /// 邮编 /// </summary> public string postcode { get; set; } /// <summary> /// 固定电话 /// </summary> public string phone { get; set; } /// <summary> /// 移动电话固定电话或移动电话至少填一项 /// </summary> public string mobile { get; set; } public string branch { get; set; } } public class Receiver { /// <summary> /// 姓名 /// </summary> public string name { get; set; } /// <summary> /// 公司 /// </summary> public string company { get; set; } /// <summary> /// 严格按照国家行政区划,省市区三级,逗号分隔。示例上海市,上海市,青浦区(cod订单必填) /// </summary> public string city { get; set; } /// <summary> /// 需要将省市区划信息加上,例如:上海市,上海市,青浦区盈港东路7766号 /// </summary> public string address { get; set; } /// <summary> /// 邮编 /// </summary> public string postcode { get; set; } /// <summary> /// 固定电话 /// </summary> public string phone { get; set; } /// <summary> /// 移动电话固定电话或移动电话至少填一项 /// </summary> public string mobile { get; set; } public string branch { get; set; } } /// <summary> /// 明细集合 /// </summary> public class Items { [XmlElement("item")] public List<Item> item { get; set; } } /// <summary> /// 明细信息 /// </summary> public class Item { /// <summary> /// 商品名称 /// </summary> public string name { get; set; } /// <summary> /// 商品数量 /// </summary> public int number { get; set; } /// <summary> /// 商品备注 /// </summary> public string remark { get; set; } } } 请求方法: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.IO; using System.Collections.Specialized; using System.Net; namespace ConsoleApplication1 { /// <summary> /// POST提交 /// </summary> class HttpClient { /// <summary> /// /// </summary> /// <param name="url"></param> /// <param name="parameters"></param> /// <returns></returns> public static HttpWebResponse post(String url, IDictionary<string, string> parameters) { HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; //如果需要POST数据 if (!(parameters == null || parameters.Count == 0)) { StringBuilder buffer = new StringBuilder(); int i = 0; foreach (string key in parameters.Keys) { if (i > 0) { buffer.AppendFormat("&{0}={1}", key, parameters[key]); } else { buffer.AppendFormat("{0}={1}", key, parameters[key]); } i++; } byte[] data = Encoding.UTF8.GetBytes(buffer.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } } return request.GetResponse() as HttpWebResponse; } /// <summary> /// /// </summary> /// <param name="url"></param> /// <param name="data"></param> /// <returns></returns> public static String post(String url, String postdata) { try { HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; byte[] data = Encoding.UTF8.GetBytes(postdata.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } HttpWebResponse response = request.GetResponse() as HttpWebResponse; StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); string outMessage = sr.ReadToEnd(); sr.Close(); return outMessage; } catch (Exception ex) { throw ex; } } } } 要求 当前按照SDK的要求: 请求报文说明: 1. 数据传输以HTTP POST方式发送,数据字符集一律采用UTF-8 2. xmldata首先需要进行base64编码 3. validation的效验方式采用 MD5(xmldata + partnerid + 密码),这里的加号为字符串连接符号。 4. 所有参数最终均须在完成数据转换后进行URL编码。 请求报文详细解释: 1.假设partnerid为YUNDA;密码为123456;xmldata内容为 <order></order> 2.xmldata经过base64编码以后变成PG9yZGVyPjwvb3JkZXI+ 3.那么要签名的内容为PG9yZGVyPjwvb3JkZXI+YUNDA123456,经过md5后的内容就为f197e870a12528e38cb483b4e371f4ea 4.然后再对xmldata经过URL编码,得到字符串PG9yZGVyPjwvb3JkZXI%2B 5.同样需要对其他字段进行URL编码,否则可能会影响POST传递,具体请参见HTTP POST传输协议 6.最终要发送的数据为: partnerid=YUNDA&version=1.0&request=data&xmldata=PG9yZGVyPjwvb3JkZXI%2B&validation=f197e870a12528e38cb483b4e371f4ea 不拉不拉不拉,一大堆,大老爷们自己去看,这些数据转换的方法我直接贴出: using System; using System.Text; using System.Web; using System.IO; using System.Xml.Serialization; namespace ConsoleApplication1 { class DataTransform { /// <summary> /// 组装主体内容 /// </summary> /// <param name="requestVO"></param> /// <returns></returns> public static String signData(RequestVO requestVO) { String xmldata = Convert.ToBase64String(System.Text.Encoding.GetEncoding("UTF-8").GetBytes(requestVO.xmldata)); string validation = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(xmldata + requestVO.partnerid + requestVO.password, "MD5").ToLower(); string signdata = "partnerid=" + requestVO.partnerid + "&version=" + requestVO.version + "&request=" + requestVO.request + "&xmldata=" + HttpUtility.UrlEncode(xmldata) + "&validation=" + validation; return signdata; } /// <summary> /// 内容数据转换XML /// </summary> /// <param name="type"></param> /// <param name="obj"></param> /// <returns></returns> public static String obj2Xml(Type type, Object obj) { XmlSerializer xml = new XmlSerializer(type); String xmldata = ""; using (MemoryStream stream = new MemoryStream()) { try { xml.Serialize(stream, obj); xmldata = Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); } catch (Exception) { throw; } } return xmldata; } /// <summary> /// 内容清洗转换 /// </summary> /// <param name="xml"></param> /// <returns></returns> public static string xmlformat(string xml) { try { System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); System.IO.StringWriter sw = new System.IO.StringWriter(); using (System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(sw)) { writer.Indentation = 2; // the Indentation writer.Formatting = System.Xml.Formatting.Indented; doc.WriteContentTo(writer); writer.Close(); } return sw.ToString(); } catch (Exception ex) { return xml; } } } } 哈哈看了这么多了 咱还没看到请求电子面单的方法是吧 别急 这个类是我自己整合的在项目里的,大老爷们先看看有不足之处 指点指点,应该能看明白!哈哈!案例的是winfrom,这个类没有用上,方法我就不贴出来了,大佬自己去最底下载吧!!! using Commons.BLL; using Commons.Model; using Commons.Settings; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Web; using System.Xml; using System.Xml.Serialization; namespace Commons.Helpers { public class YunDaApiHelper { SettingService _settingService=new SettingService(); /// <summary> /// 韵达电子面单请求url /// </summary> private static string _createYunDaUrl; /// <summary> /// 取消韵达电子面单url /// </summary> private static string _colseYunDaUrl; //韵达ID,密码 private static string _partnerid; private static string _password; //发件人信息 public static string FHCompany; public static string FHName; public static string FHMobile; public static string FHProvinceName; public static string FHCityName; public static string FHExpAreaName; public static string FHAddress; public YunDaApiHelper() {//_createYunDaUrl = "http://orderdev.yundasys.com:10110/cus_order/order_interface/interface_receive_order__mailno.php";//测试 //_colseYunDaUrl = "http://orderdev.yundasys.com:10110/cus_order/order_interface/interface_cancel_order.php";//测试 var settings = _settingService.LoadSetting<KdniaoSettings>(); _createYunDaUrl = settings.CreateYunDaUrl; _colseYunDaUrl = settings.ColseYunDaUrl; FHCompany = settings.FHCompany; FHName = settings.FHName; FHMobile = settings.FHMobile; FHProvinceName = settings.FHProvinceName; FHCityName = settings.FHCityName; FHExpAreaName = settings.FHExpAreaName; FHAddress = settings.FHAddress; _partnerid = settings.YdPartnerId; _password = settings.YdPassword; } /// <summary> /// 申请韵达电子面单 /// </summary> /// <param name="order"></param> /// <returns></returns> public YunDaResult CreateYunDaNo(Order order) { var model = new YDOrderModel(); var send = new YDSender { name = FHName,//发货人名称 company = FHCompany,//发货人公司 mobile = FHMobile,//发货人移动电话或手机 address = FHAddress,//发货人地址,需要将省市区划信息加上,例如:上海市,上海市,青浦区盈港东路7766号 postcode = "510000", //邮编 city = FHProvinceName + FHCityName + FHExpAreaName,//严格按照国家行政区划,省市区三级,逗号分隔。示例上海市,上海市,青浦区(cod订单必填) phone = "",//固话 branch = "" }; order.sender = send; model.order = order; try { var xml = Obj2Xml(typeof (YDOrderModel), model); var requestVo = new YunDaRequestModel { xmldata = xml, partnerid = _partnerid, password = _password, version = "1.0", request = "data" }; var data = SignData(requestVo); var result = Post(_createYunDaUrl, data); var msgBody = new XmlDocument(); msgBody.LoadXml(result); var status = GetXmlValue(msgBody, "status"); var dto = new YunDaResult { status = Convert.ToInt32(status), order_serial_no = GetXmlValue(msgBody, "order_serial_no"), msg = GetXmlValue(msgBody, "msg"), mail_no = GetXmlValue(msgBody, "mail_no") }; return dto; } catch (Exception ex) { var dto = new YunDaResult { status = (int) CustomBoolean.False, msg = ex.ToString() }; return dto; } } /// <summary> /// 取消韵达电子面单 /// </summary> /// <param name="xml"></param> /// <returns></returns> public YunDaResult ColseYunDaNo(string xml) { var requestVo = new YunDaRequestModel { xmldata = Xmlformat(xml), partnerid = _partnerid, password = _password, version = "1.0", request = "cancel_order" }; try { var data = SignData(requestVo); var result = Post(_colseYunDaUrl, data); var msgBody = new XmlDocument(); msgBody.LoadXml(result); var dto = new YunDaResult { status = Convert.ToInt32(GetXmlValue(msgBody, "status")), order_serial_no = GetXmlValue(msgBody, "order_serial_no"), msg = GetXmlValue(msgBody, "msg") }; return dto; } catch (Exception ex) { var dto = new YunDaResult { status = (int) CustomBoolean.False, msg = ex.ToString() }; return dto; } } #region 组装数据以及转化xml数据 /// <summary> /// 组装主体内容 /// </summary> /// <param name="requestVo"></param> /// <returns></returns> public static string SignData(YunDaRequestModel requestVo) { var xmldata = Convert.ToBase64String(System.Text.Encoding.GetEncoding("UTF-8").GetBytes(requestVo.xmldata)); var validation = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(xmldata + requestVo.partnerid + requestVo.password, "MD5").ToLower(); var signdata = "partnerid=" + requestVo.partnerid + "&version=" + requestVo.version + "&request=" + requestVo.request + "&xmldata=" + HttpUtility.UrlEncode(xmldata) + "&validation=" + validation; return signdata; } /// <summary> /// 内容数据转换XML /// </summary> /// <param name="type"></param> /// <param name="obj"></param> /// <returns></returns> public static string Obj2Xml(Type type, object obj) { var xml = new XmlSerializer(type); var xmldata = ""; using (var stream = new MemoryStream()) { try { xml.Serialize(stream, obj); xmldata = Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); } catch (Exception) { throw; } } return xmldata; } /// <summary> /// 内容清洗转换 /// </summary> /// <param name="xml"></param> /// <returns></returns> public static string Xmlformat(string xml) { try { var doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); var sw = new System.IO.StringWriter(); using (var writer = new System.Xml.XmlTextWriter(sw)) { writer.Indentation = 2; // the Indentation writer.Formatting = System.Xml.Formatting.Indented; doc.WriteContentTo(writer); writer.Close(); } return sw.ToString(); } catch (Exception ex) { return xml; } } #endregion #region Post数据请求 public static HttpWebResponse Post(string url, IDictionary<string, string> parameters) { var request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; //如果需要POST数据 if (!(parameters == null || parameters.Count == 0)) { var buffer = new StringBuilder(); var i = 0; foreach (var key in parameters.Keys) { buffer.AppendFormat(i > 0 ? "&{0}={1}" : "{0}={1}", key, parameters[key]); i++; } var data = Encoding.UTF8.GetBytes(buffer.ToString()); using (var stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } } return request.GetResponse() as HttpWebResponse; } public static string Post(string url, string postdata) { try { var request = WebRequest.Create(url) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; var data = Encoding.UTF8.GetBytes(postdata.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } var response = request.GetResponse() as HttpWebResponse; var sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); var outMessage = sr.ReadToEnd(); sr.Close(); return outMessage; } catch (Exception ex) { throw ex; } } #endregion #region MyRegion /// <summary> /// XML读取对应的值 /// </summary> /// <param name="msgBody">xml</param> /// <param name="nodeName">节点名称</param> /// <returns>返回节点值</returns> public static string GetXmlValue(XmlDocument msgBody, string nodeName) { var fromUserName = msgBody.GetElementsByTagName(nodeName).Item(0); return fromUserName?.InnerText; } #endregion } } SettingService 这个是系统配置参数,应该没毛病哈哈!so,下边咱来看看案例的界面 注 账号:韵达的客户号 密码:是韵达二维码VIP客户端的《接口联调密码》 结语 案例很简单,但是有包含蛮多东东的,各位大佬只要是搞通一个,那估摸着就都没问题了! 链接:https://pan.baidu.com/s/1T3X8-TLorn5R8nZfpKkqOg 密码:m645 ------地址要是挂了,各位直接联系我哈! 好了!各位大老爷觉着这篇文章要是不错就点个赞咯

优秀的个人博客,低调大师

C++按要求随机生成数据并写入文件

题目要求: (1) 用名称、人口、海拔高度、天气、年份等数据成员建立一个名为City的类。建立一个产生City对象的类。 (2)将产生的City对象(数量大于200个)填充至一个容器,容器的类型自选。 (3)对于City对象的具体属性值通过创建发生器来生成。生成规则如下:年份为2006年;名称由4-8个英文小写字符随机构成;人口在范围[100000,10000000)内随机选取;海拔高度在范围[0,4000)米内随机选取;上述三值均不可重复;天气在枚举常量表中{Rainy,Snowy,Cloudy,Sunny}随机选取(1年天气取12个值,即每月一个值)。 (4)容器填充完毕后,将其内容写入一个名为City.txt的文件。 代码实现如下: #include<iostream> #include<vector> #include<string> #include<time.h> #include<algorithm> using namespace std; //天气在枚举常量中表示 enum Enum_weather { Rainy=0,Snowy,Cloudy,Sunny }; //创建城市类 class City { public: int year; //char *name; string name; int persons; int elevation; char *weather[12]; City() { //neme=new char[10]; } ~City(){} }; //随机生成字符串函数 char *rand_str(char *str) { int i,n; n=rand()%5; for(i=0;i<n+4;++i) { str[i]='a'+rand()%26; } str[++i]='\\0'; return str; } //枚举类型转化为char* static inline char* weather_str(enum Enum_weather w) { char *strings[] = {"Rainy", "Snowy", "Cloudy", "Sunny",}; return strings[w]; } int main() { srand((unsigned)time(NULL)); vector<City>ve(215); for (int i=0;i<215;i++) //将随机获取每个城市的数据写入vector中 { ve[i].year=2006; ve[i].persons=rand()%(10000000 - 100000 + 1) + 100000; //在某范围内随机获取数值 ve[i].elevation=rand()%(4000-0+1)+0; char name1[10]={}; rand_str(name1); //调用rand_str()函数随机生成城市名称字符串 ve[i].name=name1; for (int j=0;j<12;j++) { int n=rand()%4; Enum_weather w1=(enum Enum_weather)(n); char* nc=weather_str(w1); //调用weather_str()函数将枚举类型值转化为char* ve[i].weather[j]=nc; } } //将2006年各个城市的数据信息写入City.txt文本文件中 FILE *fp; fp=fopen("D://City.txt","w+"); fprintf(fp,"年份 城市名称 城市人口 海拔高度 1月天气 2月天气 3月天气 4月天气 5月天气 6月天气 7月天气 8月天气 9月天气 10月天气 11月天气 12月天气\n"); for (int i=0;i<215;i++) { char name2[10]={}; for (int k=0;k<ve[i].name.size();k++) //string类型转char数组 { name2[k]=ve[i].name[k]; } fprintf(fp,"%d%10s%10d%10d",ve[i].year,name2,ve[i].persons,ve[i].elevation); for (int j=0;j<12;j++) { fprintf(fp,"%11s",ve[i].weather[j]); } fprintf(fp,"\n"); } fclose(fp); }

优秀的个人博客,低调大师

使用C# (.NET Core) 实现命令设计模式 (Command Pattern)

本文的概念内容来自深入浅出设计模式一书. 项目需求 有这样一个可编程的新型遥控器, 它有7个可编程插槽, 每个插槽可连接不同的家用电器设备. 每个插槽对应两个按钮: 开, 关(ON, OFF). 此外还有一个全局的取消按钮(UNDO). 现在客户想使用这个遥控器来控制不同厂家的家用电器, 例如电灯, 热水器, 风扇, 音响等等. 客户提出让我编写一个接口, 可以让这个遥控器控制插在插槽上的一个或一组设备. 看一下目前各家厂商都有哪些家用电器: 问题来了, 这些家用电器并没有共同的标准....几乎各自都有自己的一套控制方法.. 而且以后还要添加很多种家用电器. 设计思路 那就需要考虑一下设计方案了: 首先要考虑分离关注点(Separation of concerns), 遥控器应该可以解释按钮动作并可以发送请求, 但是它不应该了解家用电器和如何开关家用电器等. 但是目前遥控器只能做开关功能, 那么怎么让它去控制电灯或者音响呢? 我们不想让遥控器知道这些具体的家用电器, 更不想写出下面的代码: if slot1 == Light then Light.On() else if slot1 == Hub.... 说到这就不得不提到命令模式(Command Pattern)了. 命令模式允许你把动作的请求者和动作的实际执行者解耦. 这里, 动作的请求者就是遥控器, 而执行动作的对象就是某个家用电器. 这是怎么解耦的呢? 怎么可能实现呢? 这就需要引进"命令对象(command object)"了. 命令对象会封装在某个对象上(例如卧室的灯)执行某个动作的请求(例如开灯). 所以, 如果我们为每一个按钮都准备一个命令对象, 那么当按钮被按下的时候, 我们就会调用这个命令对象去执行某些动作. 遥控器本身并不知道具体执行的动作是什么, 它只是有一个命令对象, 这个命令对象知道去对哪些电器去做什么样的操作. 就这样, 遥控器和电灯解耦了. 一个命令模式的实际例子 一个快餐厅: 客户给服务员订单, 服务员把订单放到柜台并说: "有新订单了", 然后厨师按照订单准备饭菜. 让我们仔细分析一下它们是怎么交互的: 客户来了, 说我想要汉堡, 奶酪....就是创建了一个订单 (createOrder()). 订单上面写着客户想要的饭菜. 服务员取得订单 takeOrder(), 把订单拿到柜台喊道: "有新订单了"(调用orderUp()) 厨师按照订单的指示把饭菜做好 (orderUp()里面的动作). 分析一下这个例子的角色和职责: 订单里封装了做饭菜的请求. 可以把订单想象成一个对象, 这个对象就像是对做饭这个动作的请求. 并且它可以来回传递. 订单实现了一个只有orderUp()方法的接口, 这个方法里面封装了做饭的操作流程. 订单同时对动作实施者的引用(厨师). 因为都封装了, 所以服务员不知道订单里面有啥也不知道厨师是谁. 服务员只传递订单, 并调用orderUp(). 所以, 服务员的工作就是传递订单并且调用orderUp(). 服务员的取订单takeOrder()方法会传进来不同的参数(不同客户的不同订单), 但是这不是问题, 因为她知道所有的订单都支持orderUp()方法. 厨师知道如何把饭做好. 一旦服务员调用了orderUp(), 厨师就接管了整个工作把饭菜做好. 但是服务员和厨师是解耦的: 服务员只有订单, 订单里封装着饭菜, 服务员只是调用订单上的一个方法而已. 同样的, 厨师只是从订单上收到指令, 他从来不和服务员直接接触. 项目设计图 回到我们的需求, 参考快餐店的例子, 使用命令模式做一下设计: 客户Client创建了一个命令(Command)对象. 相当于客人拿起了一个订单(点菜)准备开始点菜, 我在琢磨遥控器的槽需要插哪些家用电器. 命令对象和接收者是绑定在一起的. 相当于菜单和厨师, 遥控器的插槽和目标家用电器. 命令对象只有一个方法execute(), 里面封装了调用接收者实际控制操作的动作. 相当于饭店订单的orderUp(). 客户调用setCommand()方法. 相当于客户想好点什么菜了, 就写在订单上面了. 我也想好遥控器要控制哪些家电了, 列好清单了. 调用者拿着已经setCommand的命令对象, 在未来某个时间点调用命令对象上面的execute()方法. 相当于服务员拿起订单走到柜台前, 大喊一声: "有订单来了, 开始做菜吧". 相当于我把遥控器和设备的接口连接上了, 准备开始控制. 最后接收者执行动作. 相当于厨师做饭. 家用电器使用自己独有的控制方法进行动作. 这里面: 客户 --- 饭店客人, 我 命令 --- 订单, 插槽 调用者--- 服务员, 遥控器 setCommand()设置命令 --- takeOrder() 取订单, 插上需要控制的电器 execute() 执行 --- orderUp() 告诉柜台做饭, 按按钮 接收者 --- 厨师, 家电 代码实施 所有命令对象需要实现的接口: namespace CommandPattern.Abstractions { public interface ICommand { void Execute(); } } 一盏灯: using System; namespace CommandPattern.Devices { public class Light { public void On() { Console.WriteLine("Light is on"); } public void Off() { Console.WriteLine("Light is off"); } } } 控制灯打开的命令: using CommandPattern.Abstractions; using CommandPattern.Devices; namespace CommandPattern.Commands { public class LightOnCommand : ICommand { private readonly Light light; public LightOnCommand(Light light) { this.light = light; } public void Execute() { this.light.On(); } } } 车库门: using System; namespace CommandPattern.Devices { public class GarageDoor { public void Up() { Console.WriteLine("GarageDoor is opened."); } public void Down() { Console.WriteLine("GarageDoor is closed."); } } } 收起车库门命令: using CommandPattern.Abstractions; using CommandPattern.Devices; namespace CommandPattern.Commands { public class GarageDoorOpen : ICommand { private readonly GarageDoor garageDoor; public GarageDoorOpen(GarageDoor garageDoor) { this.garageDoor = garageDoor; } public void Execute() { garageDoor.Up(); } } } 简易的遥控器: using CommandPattern.Abstractions; namespace CommandPattern.RemoteControls { public class SimpleRemoteControl { public ICommand Slot { get; set; } public void ButtonWasPressed() { Slot.Execute(); } } } 运行测试: using System; using CommandPattern.Commands; using CommandPattern.Devices; using CommandPattern.RemoteControls; namespace CommandPattern { class Program { static void Main(string[] args) { var remote = new SimpleRemoteControl(); var light = new Light(); var lightOn = new LightOnCommand(light); remote.Slot = lightOn; remote.ButtonWasPressed(); var garageDoor = new GarageDoor(); var garageDoorOpen = new GarageDoorOpenCommand(garageDoor); remote.Slot = garageDoorOpen; remote.ButtonWasPressed(); } } } 命令模式定义 命令模式把请求封装成一个对象, 从而可以使用不同的请求对其它对象进行参数化, 对请求排队, 记录请求的历史, 并支持取消操作. 类图: 效果图: 全功能代码的实施 遥控器: using System.Text; using CommandPattern.Abstractions; using CommandPattern.Commands; namespace CommandPattern.RemoteControls { public class RemoteControl { private ICommand[] onCommands; private ICommand[] offCommands; public RemoteControl() { onCommands = new ICommand[7]; offCommands = new ICommand[7]; var noCommand = new NoCommand(); for (int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } } public void SetCommand(int slot, ICommand onCommand, ICommand offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void OnButtonWasPressed(int slot) { onCommands[slot].Execute(); } public void OffButtonWasPressed(int slot) { offCommands[slot].Execute(); } public override string ToString() { var sb = new StringBuilder("\n------------Remote Control-----------\n"); for(int i =0; i< onCommands.Length; i++){ sb.Append($"[slot{i}] {onCommands[i].GetType()}\t{offCommands[i].GetType()} \n"); } return sb.ToString(); } } } 这里面有一个NoCommand, 它是一个空的类, 只是为了初始化command 以便以后不用判断是否为null. 关灯: using CommandPattern.Abstractions; using CommandPattern.Devices; namespace CommandPattern.Commands { public class LightOffCommand: ICommand { private readonly Light light; public LightOffCommand(Light light) { this.light = light; } public void Execute() { light.Off(); } } } 下面试一个有点挑战性的, 音响: namespace CommandPattern.Devices { public class Stereo { public void On() { System.Console.WriteLine("Stereo is on."); } public void Off() { System.Console.WriteLine("Stereo is off."); } public void SetCD() { System.Console.WriteLine("Stereo is set for CD input."); } public void SetVolume(int volume) { System.Console.WriteLine($"Stereo's volume is set to {volume}"); } } } 音响打开命令: using CommandPattern.Abstractions; namespace CommandPattern.Devices { public class StereoOnWithCDCommand : ICommand { private readonly Stereo stereo; public StereoOnWithCDCommand(Stereo stereo) { this.stereo = stereo; } public void Execute() { stereo.On(); stereo.SetCD(); stereo.SetVolume(10); } } } 测试运行: using System; using CommandPattern.Commands; using CommandPattern.Devices; using CommandPattern.RemoteControls; namespace CommandPattern { class Program { static void Main(string[] args) { var remote = new RemoteControl(); var light = new Light(); var lightOn = new LightOnCommand(light); var lightOff = new LightOffCommand(light); var garageDoor = new GarageDoor(); var garageDoorOpen = new GarageDoorOpenCommand(garageDoor); var garageDoorClose = new GarageDoorCloseCommand(garageDoor); var stereo = new Stereo(); var stereoOnWithCD = new StereoOnWithCDCommand(stereo); var stereoOff = new StereoOffCommand(stereo); remote.SetCommand(0, lightOn, lightOff); remote.SetCommand(1, garageDoorOpen, garageDoorClose); remote.SetCommand(2, stereoOnWithCD, stereoOff); System.Console.WriteLine(remote); remote.OnButtonWasPressed(0); remote.OffButtonWasPressed(0); remote.OnButtonWasPressed(1); remote.OffButtonWasPressed(1); remote.OnButtonWasPressed(2); remote.OffButtonWasPressed(2); } } } 该需求的设计图: 还有一个问题...取消按钮呢? 实现取消按钮 1. 可以在ICommand接口里面添加一个undo()方法, 然后在里面执行上一次动作相反的动作即可: namespace CommandPattern.Abstractions { public interface ICommand { void Execute(); void Undo(); } } 例如开灯: using CommandPattern.Abstractions; using CommandPattern.Devices; namespace CommandPattern.Commands { public class LightOnCommand : ICommand { private readonly Light light; public LightOnCommand(Light light) { this.light = light; } public void Execute() { light.On(); } public void Undo() { light.Off(); } } } 遥控器: using System.Text; using CommandPattern.Abstractions; using CommandPattern.Commands; namespace CommandPattern.RemoteControls { public class RemoteControlWithUndo { private ICommand[] onCommands; private ICommand[] offCommands; private ICommand undoCommand; public RemoteControlWithUndo() { onCommands = new ICommand[7]; offCommands = new ICommand[7]; var noCommand = new NoCommand(); for (int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } public void SetCommand(int slot, ICommand onCommand, ICommand offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void OnButtonWasPressed(int slot) { onCommands[slot].Execute(); undoCommand = onCommands[slot]; } public void OffButtonWasPressed(int slot) { offCommands[slot].Execute(); undoCommand = offCommands[slot]; } public void UndoButtonWasPressed() { undoCommand.Undo(); } public override string ToString() { var sb = new StringBuilder("\n------------Remote Control-----------\n"); for(int i =0; i< onCommands.Length; i++){ sb.Append($"[slot{i}] {onCommands[i].GetType()}\t{offCommands[i].GetType()} \n"); } return sb.ToString(); } } } 测试一下: using System; using CommandPattern.Commands; using CommandPattern.Devices; using CommandPattern.RemoteControls; namespace CommandPattern { class Program { static void Main(string[] args) { var remote = new RemoteControl(); var light = new Light(); var lightOn = new LightOnCommand(light); var lightOff = new LightOffCommand(light); var stereo = new Stereo(); var stereoOnWithCD = new StereoOnWithCDCommand(stereo); var stereoOff = new StereoOffCommand(stereo); remote.SetCommand(0, lightOn, lightOff); remote.SetCommand(1, stereoOnWithCD, stereoOff); System.Console.WriteLine(remote); remote.OnButtonWasPressed(0); remote.OffButtonWasPressed(0); remote.OnButtonWasPressed(1); remote.OffButtonWasPressed(1); } } } 基本是OK的, 但是有点小问题, 音响的开关状态倒是取消了, 但是它的音量(也包括播放介质, 不过这个我就不去实现了)并没有恢复. 下面就来处理一下这个问题. 修改Stereo: namespace CommandPattern.Devices { public class Stereo { public Stereo() { Volume = 5; } public void On() { System.Console.WriteLine("Stereo is on."); } public void Off() { System.Console.WriteLine("Stereo is off."); } public void SetCD() { System.Console.WriteLine("Stereo is set for CD input."); } private int volume; public int Volume { get { return volume; } set { volume = value; System.Console.WriteLine($"Stereo's volume is set to {volume}"); } } } } 命令: using CommandPattern.Abstractions; namespace CommandPattern.Devices { public class StereoOnWithCDCommand : ICommand { private int previousVolume; private readonly Stereo stereo; public StereoOnWithCDCommand(Stereo stereo) { this.stereo = stereo; previousVolume = stereo.Volume; } public void Execute() { stereo.On(); stereo.SetCD(); stereo.Volume = 10; } public void Undo() { stereo.Volume = previousVolume; stereo.SetCD(); stereo.Off(); } } } 运行: 需求变更----一个按钮控制多个设备的多个动作 Party Mode (聚会模式): 思路是创建一种命令, 它可以执行多个其它命令 MacroCommand: using CommandPattern.Abstractions; namespace CommandPattern.Commands { public class MacroCommand : ICommand { private ICommand[] commands; public MacroCommand(ICommand[] commands) { this.commands = commands; } public void Execute() { for (int i = 0; i < commands.Length; i++) { commands[i].Execute(); } } public void Undo() { for (int i = 0; i < commands.Length; i++) { commands[i].Undo(); } } } } 使用这个MacroCommand: using System; using CommandPattern.Abstractions; using CommandPattern.Commands; using CommandPattern.Devices; using CommandPattern.RemoteControls; namespace CommandPattern { class Program { static void Main(string[] args) { var light = new Light(); var lightOn = new LightOnCommand(light); var lightOff = new LightOffCommand(light); var garageDoor = new GarageDoor(); var garageDoorOpen = new GarageDoorOpenCommand(garageDoor); var garageDoorClose = new GarageDoorCloseCommand(garageDoor); var stereo = new Stereo(); var stereoOnWithCD = new StereoOnWithCDCommand(stereo); var stereoOff = new StereoOffCommand(stereo); var macroOnCommand = new MacroCommand(new ICommand[] { lightOn, garageDoorOpen, stereoOnWithCD }); var macroOffCommand = new MacroCommand(new ICommand[] { lightOff, garageDoorClose, stereoOff }); var remote = new RemoteControl(); remote.SetCommand(0, macroOnCommand, macroOffCommand); System.Console.WriteLine(remote); System.Console.WriteLine("--- Pushing Macro on ---"); remote.OnButtonWasPressed(0); System.Console.WriteLine("--- Pushing Macro off ---"); remote.OffButtonWasPressed(0); } } } 命令模式实际应用举例 请求队列 这个工作队列是这样工作的: 你添加命令到队列的结尾, 在队列的另一端有几个线程. 线程这样工作: 它们从队列移除一个命令, 调用它的execute()方法, 然后等待调用结束, 然后丢弃这个命令再获取一个新的命令. 这样我们就可以把计算量限制到固定的线程数上面了. 工作队列和做工作的对象也是解耦的. 记录请求 这个例子就是使用命令模式记录请求动作的历史, 如果出问题了, 可以按照这个历史进行恢复. 其它 这个系列的代码我放在这里了:https://github.com/solenovex/Head-First-Design-Patterns-in-CSharp 下面是我的关于ASP.NET Core Web API相关技术的公众号--草根专栏:

优秀的个人博客,低调大师

(C#)把磁盘目录树加载在窗体菜单中

这又是一个没有技术含量的代码。写出来只是玩玩,所以也不敢放在首页。这里有个问题,是获取文件/文件夹的图标。使用 System.Drawing.Icon.ExtractAssociatedIcon 只能获取大图标(不知道有没有高手能做到取小图标)。所以只能使用API了。设计一个这样的 ExtractIcon 类,提供一个静态方法 GetIcon ,用于获取小图标: ExtractIcon.cs 然后我们通过 ToolStripMenuItem 的 DropDownOpening 事件,加载它的子文件/文件夹。代码比较简单: Form1.cs 源代码:/Files/lemony/FileMenu.rar

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册