大概看了下C#官方提供的IObservable接口以及IObserver接口来实现发布和订阅,写的很标准,很有代表性,做下笔记,以后要是项目需要用到发布订阅再基于自己的需求改:
public class BaggageInfo { private int flightNo; private string origin; private int location; internal BaggageInfo(int flight, string from, int carousel) { this.flightNo = flight; this.origin = from; this.location = carousel; } public int FlightNumber { get { return this.flightNo; } } public string From { get { return this.origin; } } public int Carousel { get { return this.location; } } } /// /// 发布者 /// public class BaggageHandler : IObservable<BaggageInfo> { private List<IObserver<BaggageInfo>> observers; private List<BaggageInfo> flights; public BaggageHandler() { observers = new List<IObserver<BaggageInfo>>(); flights = new List<BaggageInfo>(); } public IDisposable Subscribe(IObserver<BaggageInfo> observer) { // Check whether observer is already registered. If not, add it if (! observers.Contains(observer)) { observers.Add(observer); // Provide observer with existing data. foreach (var item in flights) observer.OnNext(item); } return new Unsubscriber<BaggageInfo>(observers, observer); } // Called to indicate all baggage is now unloaded. public void BaggageStatus(int flightNo) { BaggageStatus(flightNo, String.Empty, 0); } public void BaggageStatus(int flightNo, string from, int carousel) { var info = new BaggageInfo(flightNo, from, carousel); // Carousel is assigned, so add new info object to list. if (carousel > 0 && ! flights.Contains(info)) { flights.Add(info); foreach (var observer in observers) observer.OnNext(info); } else if (carousel == 0) { // Baggage claim for flight is done var flightsToRemove = new List<BaggageInfo>(); foreach (var flight in flights) { if (info.FlightNumber == flight.FlightNumber) { flightsToRemove.Add(flight); foreach (var observer in observers) observer.OnNext(info); } } foreach (var flightToRemove in flightsToRemove) flights.Remove(flightToRemove); flightsToRemove.Clear(); } } public void LastBaggageClaimed() { foreach (var observer in observers) observer.OnCompleted(); observers.Clear(); } } internal class Unsubscriber<BaggageInfo> : IDisposable { private List<IObserver<BaggageInfo>> _observers; private IObserver<BaggageInfo> _observer; internal Unsubscriber(List<IObserver<BaggageInfo>> observers, IObserver<BaggageInfo> observer) { this._observers = observers; this._observer = observer; } public void Dispose() { if (_observers.Contains(_observer)) _observers.Remove(_observer); } } /// /// 订阅者 /// public class ArrivalsMonitor : IObserver<BaggageInfo> { private string name; private List<string> flightInfos = new List<string>(); private IDisposable cancellation; private string fmt = "{0,-20} {1,5} {2, 3}"; public ArrivalsMonitor(string name) { if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("The observer must be assigned a name."); this.name = name; } public virtual void Subscribe(BaggageHandler provider) { cancellation = provider.Subscribe(this); } public virtual void Unsubscribe() { cancellation.Dispose(); flightInfos.Clear(); } public virtual void OnCompleted() { flightInfos.Clear(); } // No implementation needed: Method is not called by the BaggageHandler class. public virtual void OnError(Exception e) { // No implementation. } // Update information. public virtual void OnNext(BaggageInfo info) { bool updated = false; // Flight has unloaded its baggage; remove from the monitor. if (info.Carousel == 0) { var flightsToRemove = new List<string>(); string flightNo = String.Format("{0,5}", info.FlightNumber); foreach (var flightInfo in flightInfos) { if (flightInfo.Substring(21, 5).Equals(flightNo)) { flightsToRemove.Add(flightInfo); updated = true; } } foreach (var flightToRemove in flightsToRemove) flightInfos.Remove(flightToRemove); flightsToRemove.Clear(); } else { // Add flight if it does not exist in the collection. string flightInfo = String.Format(fmt, info.From, info.FlightNumber, info.Carousel); if (! flightInfos.Contains(flightInfo)) { flightInfos.Add(flightInfo); updated = true; } } if (updated) { flightInfos.Sort(); Console.WriteLine("Arrivals information from {0}", this.name); foreach (var flightInfo in flightInfos) Console.WriteLine(flightInfo); Console.WriteLine(); } } } using System; using System.Collections.Generic; public class Example { public static void Main() { //发布者 BaggageHandler provider = new BaggageHandler(); // 订阅者 ArrivalsMonitor observer1 = new ArrivalsMonitor("BaggageClaimMonitor1"); // 订阅者 ArrivalsMonitor observer2 = new ArrivalsMonitor("SecurityExit"); // 发布 provider.BaggageStatus(712, "Detroit", 3); //订阅 observer1.Subscribe(provider); provider.BaggageStatus(712, "Kalamazoo", 3); provider.BaggageStatus(400, "New York-Kennedy", 1); provider.BaggageStatus(712, "Detroit", 3); observer2.Subscribe(provider); provider.BaggageStatus(511, "San Francisco", 2); provider.BaggageStatus(712); observer2.Unsubscribe(); provider.BaggageStatus(400); provider.LastBaggageClaimed(); } }
visual studio code .net 开发
Visual Studio确实是相当好用,各种简化操作什么的简直不要太舒服。但其容量太大,有时不是很方便,所以今天简单介绍一下另一个工具--Visual Studio Code.
虽然相比于老大哥Visual Studio,VS Code有很多功能不完善,但它也更灵活轻便。并且VS Code还在不断的更新当中,目前的最新版本是18年11月更新的1.30版本,包含了 多行搜索改进 等内容。
下面以.Net开发为例:
不同于Visual Studio,在VS Code上进行.Net开发你需要安装一些插件,点击左侧边栏箭头这个位置搜索
安装插件 C# (C# for Visual Studio Code (powered by OmniSharp)) (必须)
- Lightweight development tools for .NET Core.
- Great C# editing support, including Syntax Highlighting, IntelliSense, Go to Definition, Find
- All References, etc.
- Debugging support for .NET Core (CoreCLR). NOTE: Mono debugging is not supported. Desktop CLR debugging has limited support.
- Support for project.json and csproj projects on Windows, macOS and Linux.
安装插件 NuGet Package Manager (推荐,方便搜索,安装Nuget包) (推荐)
- Search the NuGet package repository for packages using either (partial or full) package name or another search term.
- Add PackageReference dependencies to your .NET Core 1.1+ .csproj or .fsproj files from Visual Studio Code's Command Palette.
- Remove installed packages from your project's .csproj or .fsproj files via Visual Studio Code's Command Palette.
- Handles workspaces with multiple .csproj or .fsproj files as well as workspaces with single .csproj/.fsproj files.
- For example:
- Ctrl + P Then Shift + > Choose "Nuget Package Manager: Add Package". Then Input the keyword about the Nuget Package. Finally, Choose the project you want to add the Nuget package.
VSCode 安装插件后一般需要重新激活以启用插件。由于VSCode本身不断更新,对于某些版本的VSCode可能需要重启应用,才能激活插件。
在VSCode中运行调试前,需要在.vscode路径下额外配置两个文件 launch.json 和 tasks.json, 来告诉vscode如何启动项目。
Launch.json:
{ // Use IntelliSense to find out which attributes exist for C# debugging // Use hover for the description of the existing attributes // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md "version": "0.2.0", "configurations": [ { "name": ".NET Core Launch (web)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceRoot}/MyABP.Web/bin/Debug/netcoreapp2.1/MyABP.Web.dll", "args": [], "cwd": "${workspaceRoot}/MyABP.Web", "stopAtEntry": false, "internalConsoleOptions": "openOnSessionStart", "launchBrowser": { "enabled": true, "args": "${auto-detect-url}", "windows": { "command": "cmd.exe", "args": "/C start ${auto-detect-url}" }, "osx": { "command": "open" }, "linux": { "command": "xdg-open" } }, "env": { "ASPNETCORE_ENVIRONMENT": "Development" }, "sourceFileMap": { "/Views": "${workspaceRoot}/Views" } }, { "name": ".NET Core Attach", "type": "coreclr", "request": "attach", "processId": "${command:pickProcess}" } ] }
如果你第一次运行某个项目,你需要确保所有引用的内容都存在,如上面的MyAbp.Web.dll文件.另外,这些配置不是固定不变的,你可以根据你的需要来进行不同的配置。
如上面
"preLaunchTask": "build" 指定了你的项目在launch之前要先进行build操作。
又如
"env": { "ASPNETCORE_ENVIRONMENT": "Development" }
指定了你要在“哪个环境”下启动你的项目。(实际上一个项目会有多种环境的配置,举个例子 appsetings.Development.json 和 appseting.QA.json用于区分不同环境的配置,如果发现在QA环境出现了问题本地不能重现时,自然需要切换到目标环境来进行调试)
Tasks.json:
{ "version": "0.1.0", "command": "dotnet", "isShellCommand": true, "args": [], "tasks": [ { "taskName": "build", "args": [ "${workspaceRoot}/MyABP.Web/MyABP.Web.csproj" ], "isBuildCommand": true, "problemMatcher": "$msCompile" } ] }
这里可以配置一下Task,如上面的
"preLaunchTask": "build" 具体的任务流程即在这里配置。
这里除去笔者使用的Web项目的配置外,当然还可以有多种其他项目应用的配置,如
这些配置完成之后,点击如图所示这个位置进入调试面板,然后点击上面的绿色三角就可以开始你的调试啦
/ **************************************************分割线*************************************************************/
vscode下常用DotNet命令
dotnet所使用的命令都可以使用 --help的方式来查看更详细的用法。
如dotnet --help
在项目开发过程中,常用的命令有
dotnet new
用于新建内容,dotnet提供了许多模板,如Console Application, Class Library等等。
使用dotnet new时需要注意新建的项目是基于.Net Framework还是基于.NetCore.
可以使用 -f 来指定.
dotnet restore
主要是寻找当前目录下的项目文件(project.json),然后利用NuGet库还原整个项目的依赖库,然后遍历每个目录,生成项目文件,继续还原该项目文件中的依赖项 --CSDN yangzhenping
以下命令不解释了,可以使用 --help 查看具体用法
dotnet build
dotnet run
dotnet publish
设计模式之☞策略模式
策略模式:它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
今天来通过一个案例,来讲一下商场的促销案例。一般商场会有那些活动呢?总结了下,一般会有这3种促销活动:1、正常收费;2、打折;3、满多少返多少
面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的抽象集合才是类。
现金收费抽象类:
1 abstract class CashSupper //现金收费抽象类 2 { 3 public abstract double acceptCash(double money);//现金收取类的抽象方法,收取现金,参数为原价,返回为当前价 4 }
正常收费子类:
1 class CashNormal : CashSupper //正常收费子类 2 { 3 public override double acceptCash(double money) 4 { 5 return money; 6 } 7 }
打折收费子类:
1 class CashRebate : CashSupper //打折收费子类 2 { 3 private double moneryRebate = 1d; 4 public CashRebate(string moneryRebate) 5 { 6 this.moneryRebate = double.Parse(moneryRebate); //打折收费,初始化时,必须要输入折扣率,如八折,就是0.8 7 } 8 public override double acceptCash(double money) 9 { 10 return money * moneryRebate; 11 } 12 }
返利收费子类:
1 class CashReturn : CashSupper 2 { 3 private double moneryCondition = 0.0d; 4 private double MoneryReturn = 0.0d; 5 public CashReturn(string moneryCondition,string moneryReturn) //返利收费,初始化时必须要输入返利条件和返利值,比如满300返100,则moneryCondition=300,moneryReturn=100 6 { 7 this.moneryCondition =double.Parse(moneryCondition); 8 this.MoneryReturn = double.Parse(moneryReturn); 9 } 10 public override double acceptCash(double money) 11 { 12 double result = money; 13 if (money>=moneryCondition) //若大于返利条件,则需要减去返利值 14 { 15 result = money - Math.Floor(money / moneryCondition) * MoneryReturn; 16 } 17 return result; 18 } 19 }
现金收费工厂类:
1 class CashFactory 2 { 3 public static CashSupper createCashAccept(string type) 4 { 5 CashSupper cs = null; 6 switch (type) 7 { 8 case "正常收费": 9 cs = new CashNormal(); 10 break; 11 case "满300反100": 12 CashReturn cr1 = new CashReturn("300","100"); 13 cs = cr1; 14 break; 15 case "打8折": 16 CashRebate cr2 = new CashRebate("0.8"); 17 cs = cr2; 18 break; 19 } 20 return cs; 21 } 22 }
CashContext类:
1 class CashContext 2 { 3 private CashSupper cs=null; //声明一个CashSuper 4 public CashContext(string type) //表示收费的类型 5 { 6 switch (type) 7 { 8 case "正常收费": 9 CashNormal cs0 = new CashNormal(); 10 cs = cs0; 11 break; 12 case "满300返100": 13 CashReturn cr1 = new CashReturn("300","100"); 14 cs = cr1; 15 break; 16 case "打8折": 17 CashRebate cr2 = new CashRebate("0.8"); //将实例化具体策略的过程由客户端转移到Context类中。简单工厂的引用 18 cs = cr2; 19 break; 20 } 21 } 22 public double GetResult(double Money) 23 { 24 return cs.acceptCash(Money); //根据收费策略的不同,获得计算结果 25 } 26 }
界面:
调用:
1 double total = 0.0d; //用户总计 2 private void btnOk_Click(object sender, EventArgs e) 3 { 4 CashContext cc = new CashContext(cmbType.SelectedItem.ToString()); 5 double totalPrices = 0d; 6 totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text)*Convert.ToDouble(txtNumber.Text)); //通过对Context的GetResult方法的调用,可以得到收取费用的结果,让具体算法与客户进行隔离 7 total = total + totalPrices; 8 listBox1.Items.Add("单价:"+txtPrice.Text+"数量:"+txtNumber.Text+" "+cmbType.SelectedItem+"合计:"+totalPrices.ToString()); 9 label5.Text = total.ToString(); 10 }
C#字符串转二进制、二进制转字符串
最近公司要做一个操作日志的模块,如果将操作日志以字符串的形式存到后台数据库,非常浪费内存,不可取,特意写了字符串与二进制相互转换的函数。
1、字符串转二进制
1 private string StringToBinary(string str) 2 { 3 byte[] data = Encoding.Unicode.GetBytes(str); 4 StringBuilder sb = new StringBuilder(data.Length*8); 5 foreach (byte item in data) 6 { 7 sb.Append(Convert.ToString(item,2).PadLeft(8,'0')); 8 } 9 return sb.ToString(); 10 }
2、二进制转字符串
1 private string BinaryToString(string str) 2 { 3 System.Text.RegularExpressions.CaptureCollection cs = System.Text.RegularExpressions.Regex.Match(str,@"([01]{8})+").Groups[1].Captures; 4 byte[] data = new byte[cs.Count]; 5 for (int i = 0; i < cs.Count; i++) 6 { 7 data[i] = Convert.ToByte(cs[i].Value,2); 8 } 9 return Encoding.Unicode.GetString(data,0,data.Length); 10 }
下面随便写了一条Sql语句,便于测试两者是否转换成功?
转码成功!
c# 接口的协变和逆变
如果派生类只是用于输出值,那么这种结构化的委托有效性之间的常数关系叫做协变
就是创建一个派生类委托对象 让派生类赋值给基类对象 协变关键字out
对期望传入基类时允许传入派生对象的特性叫逆变 逆变关键字in
看一下实现代码
class Animal { public string Name; } class Dog:Animal { } interface Im<out T>//协变 { T GetT(); } class MyClass<T> : Im<T> { public T[] item = new T[2]; public T GetT() { return item[0]; } } class Program { static void DoSomething(Im<Animal> im) { Console.WriteLine(im.GetT().Name); } static void Main(string[] args) { MyClass<Dog> myClass = new MyClass<Dog>(); myClass.item[0] = new Dog() { Name = "AVT" }; Im<Animal> ANIMAL = myClass; DoSomething(myClass); } }
c# 使用迭代器来创建可枚举类型
class Program { public IEnumerator<string> GetEnumerator() { IEnumerable<string> my = BlackAndWhite(); return my.GetEnumerator(); } public IEnumerable<string> BlackAndWhite() { yield return "black"; yield return "gray"; yield return "white"; } static void Main(string[] args) { Program program = new Program(); foreach (var item in program) { Console.WriteLine(item); } foreach (var item in program.BlackAndWhite()) { Console.WriteLine(item); } } }
c# 创建,加载,修改XML文档
using System.Xml.Linq;
static void Main(string[] args) { XDocument xDocument = new XDocument(new XElement("mployess", //创建跟元素 new XElement("name", "a"),new XElement("name","b")));//创建元素 xDocument.Save("地址"); XDocument empole = XDocument.Load("地址");//加载xml 文档 可以保证修改 Console.WriteLine(empole); }