0%

在Vista 和 Windows 7 及更新版本的操作系统,增加了 UAC(用户账户控制) 的安全机制,如果 UAC 被打开,用户即使以管理员权限登录,其应用程序默认情况下也无法对系统目录、系统注册表等可能影响系统正常运行的设置进行写操作。这个机制大大增强了系统的安全性,但对应用程序开发者来说,我们不能强迫用户去关闭UAC,但有时我们开发的应用程序又需要以 Administrator 的方式运行,如何实现这样的功能呢?

下面演示 C# 程序如何实现提示用户以管理员权限运行。

本例以WinForm程序演示,新建一项目生成后进行相应修改:

方法一:通过 System.Diagnostics.Process.Start() 方式启动:

实现方法: 修改默认生成的Program文件,修改后的代码如下:

由于已经在代码上做了注释,所以不再详细说明;

复制代码

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
31
32
33
34
35
36
37
38
39
40
41
42
43
 1     static class Program
2 {
3 [STAThread]
4 static void Main()
5 {
6 Application.EnableVisualStyles();
7 Application.SetCompatibleTextRenderingDefault(false);
8
9 /**
10 * 当前用户是管理员的时候,直接启动应用程序
11 * 如果不是管理员,则使用启动对象启动程序,以确保使用管理员身份运行
12 */
13 //获得当前登录的Windows用户标示
14 System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent();
15 System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(identity);
16 //判断当前登录用户是否为管理员
17 if (principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator))
18 {
19 //如果是管理员,则直接运行
20 Application.Run(new Form1());
21 }
22 else
23 {
24 //创建启动对象
25 System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
26 startInfo.UseShellExecute = true;
27 startInfo.WorkingDirectory = Environment.CurrentDirectory;
28 startInfo.FileName = Application.ExecutablePath;
29 //设置启动动作,确保以管理员身份运行
30 startInfo.Verb = "runas";
31 try
32 {
33 System.Diagnostics.Process.Start(startInfo);
34 }
35 catch
36 {
37 return;
38 }
39 //退出
40 Application.Exit();
41 }
42 }
43 }

复制代码

效果:由于是通过System.Diagnostics.Process.Start() 方式外部调用启动,所以直接通过VS运行时,是不会提示VS也需要管理员权限,只有程序本身需要管理员权限,与生成应用程序的程序不同。这点是和方法二实现的主要不同之处。

本文地址:http://www.cnblogs.com/Interkey/p/RunAsAdmin.html

方法二:通过添加应用程序清单文件:

在 项目 上 添加新项 选择“应用程序清单文件” 然后单击 添加 按钮

添加后,默认打开app.manifest文件,将:

修改为:

然后打开 项目属性 ,将 应用程序 标签页中的 资源 中的 清单 修改为新建的 app.manifest。

重新生成项目,再次打开程序时就会提示 需要以管理员权限运行。

需要注意的是:如果在VS中 启动调试 的话,就会提示 此任务要求应用程序具有提升的权限。如下图:

提升权限

选择 使用其他凭据重新启动 即可。

方法三:直接修改程序文件的属性

右击程序文件,在弹出的属性对话框中的 兼容性 标签页中

勾选“以管理员身份运行此程序”即可。

 设置权限等级


判断程序是否以管理员身份运行

 需要添加命名空间:

using System.Security.Principal;

复制代码

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
/// <summary>
/// 确定当前主体是否属于具有指定 Administrator 的 Windows 用户组
/// </summary>
/// <returns>如果当前主体是指定的 Administrator 用户组的成员,则为 true;否则为 false。</returns>
public static bool IsAdministrator()
{
bool result;
try
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
result = principal.IsInRole(WindowsBuiltInRole.Administrator);

//http://www.cnblogs.com/Interkey/p/RunAsAdmin.html
//AppDomain domain = Thread.GetDomain();
//domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
//WindowsPrincipal windowsPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;
//result = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
}
catch
{
result = false;
}
return result;
}

复制代码


如果有兴趣还可以继续查看下面的链接:

http://www.cnblogs.com/Lemon_s/archive/2011/07/28/2119222.html

http://www.cnblogs.com/shenchao/archive/2013/03/05/2944660.html

希望能帮到大家~~~

对于C#通过程序来调用cmd命令的操作,网上有很多类似的文章,但很多都不行,竟是漫天的拷贝。我自己测试整理了一下。

代码:

复制代码

        string str = Console.ReadLine();

        System.Diagnostics.Process p \= new System.Diagnostics.Process();
        p.StartInfo.FileName \= "cmd.exe";
        p.StartInfo.UseShellExecute \= false;    //是否使用操作系统shell启动
        p.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
        p.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
        p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
        p.StartInfo.CreateNoWindow = true;//不显示程序窗口
        p.Start();//启动程序 //向cmd窗口发送输入信息
        p.StandardInput.WriteLine(str + "&exit");

        p.StandardInput.AutoFlush \= true; //p.StandardInput.WriteLine("exit"); //向标准输入写入要执行的命令。这里使用&是批处理命令的符号,表示前面一个命令不管是否执行成功都执行后面(exit)命令,如果不执行exit命令,后面调用ReadToEnd()方法会假死 //同类的符号还有&&和||前者表示必须前一个命令执行成功才会执行后面的命令,后者表示必须前一个命令执行失败才会执行后面的命令 //获取cmd窗口的输出信息
        string output = p.StandardOutput.ReadToEnd(); //StreamReader reader = p.StandardOutput; //string line=reader.ReadLine(); //while (!reader.EndOfStream) //{ // str += line + "  "; // line = reader.ReadLine(); //}

p.WaitForExit();//等待程序执行完退出进程
p.Close();

        Console.WriteLine(output);

复制代码

程序运行结果:

需要提醒注意的一个地方就是:在前面的命令执行完成后,要加exit命令,否则后面调用ReadtoEnd()命令会假死。

我在之前测试的时候没有加exit命令,输入其他命令后窗口就假死了,也没有输出内容。

对于执行cmd命令时如何以管理员身份运行,可以看我上一篇文章: C#如何以管理员身份运行程序 - 酷小孩 - 博客园

2014-7-28 新增:

另一种C#调用cmd命令的方法,不过这种方法在执行时会“闪一下” 黑窗口,各位在使用时可以按喜好来调用。

复制代码

    /// <summary>
    /// 运行cmd命令 /// 会显示命令窗口 /// </summary>
    /// <param name="cmdExe">指定应用程序的完整路径</param>
    /// <param name="cmdStr">执行命令行参数</param>
    static bool RunCmd(string cmdExe, string cmdStr)
    { bool result = false; try { using (Process myPro = new Process())
            { //指定启动进程是调用的应用程序和命令行参数
                ProcessStartInfo psi = new ProcessStartInfo(cmdExe, cmdStr);
                myPro.StartInfo \= psi;
                myPro.Start();
                myPro.WaitForExit();
                result \= true;
            }
        } catch {

        } return result;
    } /// <summary>
    /// 运行cmd命令 /// 不显示命令窗口 /// </summary>
    /// <param name="cmdExe">指定应用程序的完整路径</param>
    /// <param name="cmdStr">执行命令行参数</param>
    static bool RunCmd2(string cmdExe, string cmdStr)
    { bool result = false; try { using (Process myPro = new Process())
            {
                myPro.StartInfo.FileName \= "cmd.exe";
                myPro.StartInfo.UseShellExecute \= false;
                myPro.StartInfo.RedirectStandardInput \= true;
                myPro.StartInfo.RedirectStandardOutput \= true;
                myPro.StartInfo.RedirectStandardError \= true;
                myPro.StartInfo.CreateNoWindow \= true;
                myPro.Start();

                    //如果调用程序路径中有空格时,cmd命令执行失败,可以用双引号括起来 ,在这里两个引号表示一个引号(转义) string str = string.Format(@”””{0}”” {1} {2}”, cmdExe, cmdStr, “&exit”);

                myPro.StandardInput.WriteLine(str);
                myPro.StandardInput.AutoFlush \= true;
                myPro.WaitForExit();

                result \= true;
            }
        } catch {

        } return result;
    } 

复制代码

转载请注明出处。

FM:http://www.cnblogs.com/William\_Fire/articles/29064.html

1.  避免将多个类放在一个文件里面。

2.  一个文件应该只有一个命名空间,避免将多个命名空间放在同一个文件里面。

3.  一个文件最好不要超过500行的代码(不包括IDE产生的代码)。

4.  一个方法的代码长度最好不要超过25行。

5.  避免方法中有超过5个参数的情况。如果超过了,则应使用 struct 来传递多个参数。

6.  每行代码不要超过80个字符。

7.  原则上,尽量不要手工的修改机器产生的代码。

a)  如果需要编辑机器(IDE)产生的代码,编辑格式和风格要符合该编码标准。

b)  尽可能地使用片断类来把被保持的部分分解为各个因素

    注:这里的翻译参考了灵感之源老兄的说法,在Visual c#2005中,C#的语法已经支持partial修饰符,它的作用是可以将一个完整的类分解各个部分类,在编译时,编译器会将它们构造为一个类。

具体请参考

http://blog.joycode.com/zhanbos/archive/2004/05/25/22402.aspx 

http://weblogs.asp.net/jaybaz_ms/archive/2004/04/28/122392.aspx

8.  避免利用注释解释显而易见的代码。

a)  代码应该可以自解释。好的代码本身就应具体良好的可读性,所使用的变量和方法命名一般情况下不需要注释。

9.  文档应该仅用于assumptions, algorithm insights等等.

10.  避免使用方法级的文档。

a)  使用扩展的API文档进行说明。

b)  只有在该方法需要被其他的开发者使用的时候才使用方法级的注释。(在C#中就是///)

11.  不要硬编码数字的值,总是使用构造函数设定其值。

12.  只有是自然结构才能直接使用const(常量),比如一个星期的天数。

13.  区别只读变量及常量的使用方法,如果想实现只读变量,可以直接使用readonly 修饰符。

public class MyClass

{

   public readonly int Number;

   public MyClass(int  someValue)

   {

      Number = someValue;

   }

   public  const int  DaysInWeek = 7;

}

14.  每个假设必须使用Assert检查

a)  平均每15行要有一次检查(Assert)

using System.Diagnostics;

    object GetObject()

{…}

    object obj = GetObject();

Debug.Assert(obj != null);

15.  代码的每一行都应该通过白盒方式的测试。

16.  只抛出已经显示处理的异常。

17.  在捕获(catch)语句的抛出异常子句中(throw),总是抛出原始异常,用以维护原始错误的堆栈分配。
  catch(Exception exception)

{   

      MessageBox.Show(exception.Message);

      throw ;  //和throw exception一样。

}

  注:同理,不推荐在循环语句中,进行直接的return操作。
    for (int i=0;i<100;i++)
    {
        if (i==10)
        {
            return; //不推荐的方式
        }
    }

18.  避免方法的返回值是错误代码。

19.  尽量避免定义自定义异常类。

20.  当需要定义自定义的异常时:

a)  自定义异常要继承于ApplicationException。

b)  提供自定义的序列化功能。

21.  避免在单个程序集里使用多个Main方法。

22.  只对外公布必要的操作,其他的则为internal。

23.  避免使用友元程序集,因为它会增加程序集间的耦合度。

24.  避免编写从指定的位置加载的程序集的代码。

25.  使应用程序集尽量为最小化代码(EXE客户程序)。使用类库来替换包含的商务逻辑。

26.  避免给枚举变量提供显式的值。

//正确方法 

public enum Color

{   

   Red,Green,Blue

}

//避免

public enum Color

{   

   Red = 1,Green =  2,Blue = 3

}

27.  避免指定特殊类型的枚举变量。

//避免 

public enum Color  : long

{   

   Red,Green,Blue

}

28.  即使if语句只有一句,也要将if语句的内容用大括号扩起来。

29.  避免使用trinary条件操作符。

30.  避免在条件语句中调用返回bool值的函数。可以使用局部变量并检查这些局部变量。

bool IsEverythingOK()

{…}

//避免

if (IsEverythingOK ())

{…}

//替换方案 

bool ok = IsEverythingOK();

if (ok)

{…}

31.  总是使用基于0开始的数组。

32.  在循环中总是显式的初始化引用类型的数组。

public class MyClass

{}

MyClass[] array = new  MyClass[100];

for(int index = 0; index < array.Length;  index++)

{

   array[index] = new  MyClass();

}

33.  尽量不要提供public 和 protected的成员变量,使用属性代替他们。

34.  避免在继承中使用new而使用override来进行替换。

35.  在不是sealed的类中总是将public 和 protected的方法标记成virtual的。

36.  除非使用interop(COM+ 或其他的dll)代码否则不要使用不安全的代码(unsafe code)。

37.  避免显式的转换,使用as操作符进行兼容类型的转换。

Dog dog = new GermanShepherd();

GermanShepherd shepherd = dog  as  GermanShepherd;

if (shepherd != null )

{…}

38.  当类成员包括委托的时候

a)  在调用委托前,将它拷贝到一个本地变量中,用以避免并发争用条件。

b)  在调用委托之前一定要检查它是否为null

public class MySource

{

   public event EventHandler  MyEvent;

   public void FireEvent()

   {
   //将委托拷到一个本地变量中。
      EventHandler temp = MyEvent;

      //确定它是否为空
      if(temp != null )

      {

         temp(this,EventArgs.Empty);

      }

   }

}  

39.  不要提供公共的事件成员变量,使用事件访问器替换这些变量。

public class MySource

{

   MyDelegate m_SomeEvent ;

   public event MyDelegate SomeEvent

   {

      add

      {

         m_SomeEvent += value;

      }

      remove

      {

         m_SomeEvent -= value;

      }

   }

}

40.  使用一个事件帮助类来公布事件的定义。

41.  总是使用接口。

42.  类和接口中的方法和属性至少为2:1的比例。

43.  避免一个接口中只有一个成员。

44.  尽量使每个接口中包含3-5个成员。

45.  接口中的成员不应该超过20个。

a) 根据实际情况可能限制为12个

46.  避免接口成员中包含事件。

47.  避免使用抽象方法而使用接口替换。

48.  在类层次中显示接口。

49.  推荐使用显式的接口实现。

50.  从不假设一个类型兼容一个接口,并应防止查询那些接口。

SomeType obj1;

IMyInterface obj2;

/* 假设已有代码初始化过obj1,接下来 */

obj2 = obj1 as IMyInterface;

if (obj2 != null)

{

   obj2.Method1();

}

else

{

   //处理错误

}  

51. 表现给最终用户的字符串(一般指UI界面中的部分)不要使用直接编码,而应该要使用资源文件来替换。

          注:这样做的目的是方便软件的本地化。

52.  不要直接编写可能会更改的基于配置的字符串,比如连接字符串。

53.  当需要构建较长的字符串的时候,应该考虑使用StringBuilder不要使用string来处理。

注:string每次要创建一个新的实例,较占用空间,并产生了相对StringBuilder更大的性能消耗。对于过于频繁的字符串操作,采用StringBuilder是一个良好的习惯。

54.  避免在结构里面提供方法。

a)  建议使用参数化构造函数

b)  可以重载操作符

55.  总是要给静态变量提供静态构造函数。

56.  在能够使用早期绑定的情况下,尽量避免使用后期绑定。

   注:后期绑定虽然灵活,但带来的不仅仅是性能上的消耗,更多的是编码上的复杂性和混乱的逻辑。

57.  使用应用程序的日志和跟踪。

58.  除非在不完全的switch语句中否则不要使用goto语句。

     注:原则上不应使用goto语句,除非在能够大大减轻编码的复杂性,并不影响可读性的前提下才允许使用。

59.  在switch语句中总是要有default子句来显示信息(Assert)。

int number  = SomeMethod();

switch(number)

{

   case 1:

      Trace.WriteLine(“Case 1:”);

      break;

   case 2:

      Trace.WriteLine(“Case 2:”);

      break;

   default :

      Debug.Assert(false);

      break;

}

60.  除非在构造函数中调用其他构造函数否则不要使用this指针。

// 正确使用this的例子

public class MyClass

{

   public MyClass(string message )

   {}

   public MyClass()  : this(“hello”)

   {}

}

61.  除非你想重写子类中存在名称冲突的成员或者调用基类的构造函数否则不要使用base来访问基类的成员。

// 正确使用base的例子

public class Dog

{

   public Dog(string name)

   {}

   virtual public void Bark( int howLong)

   {}

}

public class GermanShepherd : Dog

{

   public GermanShe pherd(string name): base (name)

   {}

   override public void Bark(int  howLong) 

   {

      base .Bark(howLong);  

   }

}

62.  基于模板的时候要实现Dispose()和Finalize()两个方法。

63.  通常情况下避免有从System.Object转换来和由System.Object转换去的代码,而使用强制转换或者as操作符替换。

class SomeClass

{}

//避免:

class MyClass 

{   

   void SomeMethod(T t)   

   {

      object temp = t;      

      SomeClass obj = (SomeClass)temp;    

   }

}

// 正确:

class MyClass where T : SomeClass

{   

   void SomeMethod(T t)   

   {

      SomeClass obj = t;   

   }

}

64.  在一般情况下不要定义有限制符的接口。接口的限制级别通常可以用强类型来替换之。

public class Customer

{…}

//避免:

public interface IList where T : Customer 

{…}

//正确:

public interface ICustomerList : IList 

{…}

65.  不确定在接口内的具体方法的限制条件。

66.  总是选择使用C#内置(一般的generics)的数据结构

67、初始化类的实例时,除非十分必要,否则不要赋null值。
68、使用后的实例,尽量不要将该实例的引用赋值和为nul,尤其是采用public来修饰的类成员l。
1) 如果该实例是临时引用,请使用using语句,然后在程序块中使用。
2) 如果需要释放资源,应可能地使用Dispose,采用null值的方法,该引用在指向下一个实例前,不会被回收。

本文不但介绍了CS-Script如何部署,还介绍了CS-Script的部署后面的原理,并用一个框图详细介绍了部署中的各种细节。

一、获取资源

1.从官网上下载编译好的csscript资源:cs-script.7z 

https://csscriptsource.codeplex.com/releases/view/616234

2.解压到某目录下,注意选择一个相对固定的目录,比如一个专门用来存放库文件的目录;

比如在我的机器上会放到: E:\OpenSource\cs目录下;

二、执行安装

严格来说,csscript是不需要部署的,其实只要下载了zip包就ok了,不需要什么部署,下载了即可使用。后续的所谓安装只是对操作系统做一些调整,以便让后续编写脚本,执行脚本可以更加方便。

安装步骤:

1. 找到第一节中的解压目录,运行其根目录中的install.cmd,即可完成安装;

2. 默认情况下CS-Script的库是使用.NET V4.5的(注,并不是安装程序需要.NET V4.5,而是运行程序,特别是CSScriptLibrary.dll),如果当前计算机没有安装.NET v4.5,则程序会弹出提示,或者选择降低到 V4.0来使用,那么在内部来说,就是用 lib\Bin\NET 4.0\CSScriptLibrary.dll来替代 lib目录下的CSScriptLibrary.dll。

三、Install.cmd在系统中添加了什么

1. 添加了各种环境变量的,如下图所示:

image

同时会把%CSSCRIPT_DIR%, %CSSCRIPT_DIR%\LIB添加到环境变量中,以便可以找到cscs.exe, csws.exe等各种csscript的工具;

2.在ProgramData中创建了CS-Script目录,拷贝了后续辅助调试需要用到的一些资源,主要是将后续的右键菜单需要用到的一些命令拷贝到了

C:\ProgramData\CS-Script\ShellExt目录下了,内容如下:

image

3. 注册了一个动态的右键菜单,后续只要在cs文件上右键,在右键菜单中就会包含如下内容:

这个菜单其实是根据C:\ProgramData\CS-Script\ShellExt目录下的cmd文件动态生成的。

image

四、Install.bat都做了什么?

打开install.cmd,里面就一句话:

start css_config.exe,所以我猜想所有配置的工作都封装在css_config.exe的源码中了,好在这是开源代码,在下载源码包中也包括了css_config项目的代码,所以我就顺着css_config的源码往下看,打开css_config发现他的代码很简单,主要做了三件事情:

1. 检查当前windows是否有管理员权限,如果没有则提示并退出;

2.根据当前系统的.NET 版本,以及用户的选择,确认拷贝哪个版本的CSSCriptLibrary.dll到 Lib目录下;

3.调用csws.exe来执行一段cs脚本,以执行更深层次的配置,代码如下:

string csws = Path.Combine(rootDir, “csws.exe”); string configScript = Path.Combine(rootDir, @”lib\config.cs”);
args = new string[2];
args[0] = “/dbg”;
args[1] = configScript;
AppDomain.CurrentDomain.ExecuteAssembly(Path.Combine(rootDir, @”csws.exe”), args);

后续就顺着config.cs一直往下走,发现整个部署过程的逻辑听复杂,在各种cmd、exe、dll和cs之前绕来绕去的,大致如下图所示,如果有兴趣的话,可以再详细去看具体的代码。

image

本系列包括:

C#脚本引擎 CS-Script 之(一)——初识 

C#脚本引擎 CS-Script 之(二)——性能评测 

C#脚本引擎CS-Script之(三)——如何部署

以下以一个简单的HelloWord程序为例,来分析csscript脚本引擎的性能。

复制代码

1 class HelloWorld
3 { 4 public void SayHello() 5 { 6 Console.WriteLine(“Hello World, from internal!”); 7 } 8 }

复制代码

一、测试环境

运行的机器硬件配置:Intel Dore Duo CPU,内存 4;

开发环境: vs2010;

二、使用程序内部类和使用脚本的性能比较

复制代码

1 static void Main(string[] args)
2 {
3 CallFromInternal();
4 CallFromScript();
5 }
6
7 static void CallFromInternal() 8 {
9 Console.WriteLine(“”); 10 Console.WriteLine(“CallFromInternal”); 11 DateTime beginTime = DateTime.Now; 12
13 HelloWorld hello = new HelloWorld(); 14 TimeSpan span = DateTime.Now - beginTime; 15 Console.WriteLine(“create instance timespan: {0}”, span); 16 beginTime = DateTime.Now; 17 hello.SayHello(); 18
19 span = DateTime.Now - beginTime; 20 Console.WriteLine(“call helloWorld timespan: {0}”, span); 21 } 22
23
24 static void CallFromScript() 25 { 26 Console.WriteLine(“”); 27 Console.WriteLine(“CallFromScript”); 28 DateTime beginTime = DateTime.Now; 29
30 dynamic hello = CSScript.Evaluator.LoadFile(“HelloWorld.cs”); 31 TimeSpan span = DateTime.Now - beginTime; 32 Console.WriteLine(“load and precompile script file, timespan= {0}”, span); 33
34 beginTime = DateTime.Now; 35 hello.SayHello(); 36
37 span = DateTime.Now - beginTime; 38 Console.WriteLine(“call helloWorld timespan: {0}”, span); 39 }

复制代码

从以上两个函数的输出结果来看,直接调用程序内部函数的时间大概是2ms,而通过脚本引擎来同样一个HelloWorld的时间就达到了835ms,时间差距有400倍。

这835ms中,动态编译及其对象创建就花了814ms,而函数调用则21ms,所以即使抛开动态编译的成本,这个函数调用,由于内部其实是使用反射的机制来实现的,所以性能损失也比较明显。

三、一次动态编译多次调用

测试代码:

复制代码

1 static void CallFromSameScriptLoad1TimeAndCall4Times() 2 {
3 Console.WriteLine(“”);
4 Console.WriteLine(“CallFromSameScriptLoad1TimeAndCall4Times”);
5 DateTime beginTime = DateTime.Now; 6 TimeSpan span;
7 dynamic hello = CSScript.Evaluator.LoadFile(“HelloWorld.cs”);
8 span = DateTime.Now - beginTime; 9 Console.WriteLine(“load and precompile script file, timespan= {0}”, span); 10
11 for (int i = 0; i < 4; ++i) 12 { 13 beginTime = DateTime.Now; 14 hello.SayHello(); 15 span = DateTime.Now - beginTime; 16 Console.WriteLine(“call helloWorld {0} time, timespan: {1}”, i+1, span); 17 Console.WriteLine(“”); 18 } 19 }

复制代码

运行结果如下, 可以看出,第一次调用花了21ms,后面3次调用的时间基本可以忽略。那么推测,第一次是因为需要通过反射的方式找到SayHello方法的引用,后面的几次调用估计已经把该方法的引用缓存了,就可以直接前面查找好的委托,少了一个通过反射查找的过程,所以速度基本和调用本地方法相当。

以上只是推测,后续需要查阅源码分析看看。

四、多次动态编译同一个脚本并调用方法的性能分析

测试代码:

复制代码

1 static void CallFromSameScriptLoadAndCall4Times() 2 {
3 Console.WriteLine(“”);
4 Console.WriteLine(“CallFromSameScriptLoadAndCall4Times”);
5
6 TimeSpan span;
7 for (int i = 0; i < 4; ++i)
8 {
9 DateTime beginTime = DateTime.Now; 10 dynamic hello = CSScript.Evaluator.LoadFile(“HelloWorld.cs”); 11 span = DateTime.Now - beginTime; 12 Console.WriteLine(“load and precompile script file, {0}, timespan= {1}”, i+1, span); 13 beginTime = DateTime.Now; 14 hello.SayHello(); 15 ) 16
17 span = DateTime.Now - beginTime; 18 Console.WriteLine(“call helloWorld {0} time, timespan: {1}”, i+1, span); 19 Console.WriteLine(“”); 20 } 21 }

复制代码

测试结果如下,第一次调用的时间花销大,后续的时间花销基本相当于上一节中的第一次调用方法的时间。

那么推测:

(1) 对于同一个cs源文件,第一次编译之后会缓存,会把程序集缓存到内存中,后续再调用LoadFile的时候,实际上加载的是内存中缓存的程序集;

(2)第二次及其后续调用LoadFile(“HelloWorld.cs”),实际上都是使用内存中的程序集,但是通过反射的方式找到HelloWorld类还是要每次去做的,所以一般还需要花费3ms左右;

(3)然后由于每个循环中都重新创建了一个新的HelloWord对象,在每个循环中去调用hello.SayHello();的时候,实际上还是实时的使用反射机制去查找hello对象中的SayHello方法,所以这里的时间花销省不了,一般也需要花费7ms左右。

推测:

(1)同一个程序集,多次编译,会使用第一次编译缓存的程序集;

(2)同一个类的多个对象的同一个方法(比如HelloWord类的SayHello方法),每个对象第一次调用该方法时,都需要使用反射方式去查找,所以此时性能较低;

五、动态编译多个不同的脚本的性能分析

测试代码:

复制代码

1 static void CallFromMultiScriptLoadAndCall4Times() 2 {
3 Console.WriteLine(“”);
4 Console.WriteLine(“CallFromMultiScriptLoadAndCall4Times”);
5
6 TimeSpan span;
7 for (int i = 0; i < 4; ++i)
8 {
9 DateTime beginTime = DateTime.Now; 10 string fileName = string.Format(“HelloWorld{0}.cs”, i + 1); 11 dynamic hello = CSScript.Evaluator.LoadFile(fileName); 12 span = DateTime.Now - beginTime; 13 Console.WriteLine(“load and precompile script file{2}, {0}, timespan= {1}”, i+1, span, fileName); 14 beginTime = DateTime.Now; 15 hello.SayHello(); 16 span = DateTime.Now - beginTime; 17 Console.WriteLine(“call helloWorld {0} time, timespan: {1}”, i+1, span); 18 } 19 }

复制代码

测试结果如下:

这里分别动态编译了四个源文件,并调用对应的方法。但是只有第一次编译的时候速度慢,后续三次动态编译的速度和上一节动态编译同一个源文件的速度一样快。到这里就推翻了上一节的结论,说明上一节中2~4次的动态编译速度快,不是因为缓存了第一次动态编译的程序集。那么推测可能是因为第一次要动态编译的时候,程序要将.NET的用于动态编译的程序集(CSharpCodeProvider)加载到内存中,这个过程可能比较花时间,而动态编译本身是很快的。

六、结论

(1)在使用cs-script脚本引擎的时候,该程序第一次做动态编译时,需要有个1s左右的初始化时间;

(2)对于脚本中类的对象的方法的调用,在第一次调用某个对象的方法时(比如上文的HelloWorld类的hell对象的SayHello()方法),由于要使用反射方式去查找该犯法的委托,所以相比原生的对象方法调用要多10ms左右,后续的调用则和原生的方法差不多。

(3)cs-script编译一个普通源文件的时间基本是毫秒级别,一般在10ms以内,对于一般脚本数量不是很多(比如几十个)的情况,一般也就是多花几百毫秒,基本可以忽略;

综上,在引入了cs-script脚本引擎之后,在享受了脚本所带来的动态特性的同时,只是在初始化的时候需要多花1s左右的时间,其他情况的性能损失基本可以忽略。

 七、相关源码

CSScript系列之(二)——性能评测.zip

本系列包括:

C#脚本引擎 CS-Script 之(一)——初识 

C#脚本引擎 CS-Script 之(二)——性能评测 

C#脚本引擎CS-Script之(三)——如何部署

前言

最近做的项目是监控方面的,需要对接各种摄像头,之前的方案是把各个厂家的SDK都集成到系统中,然后让用户进行切换,后来知道了Onvif (自行百度具体概念)这个东西。原来早就有人一统江湖了。

onvif 协议定义的部分wsdl文档

https://www.onvif.org/ver10/media/wsdl

https://www.onvif.org/ver10/device/wsdl

ONVIF Device Test Tool

这是一个Onvif的测试工具(自行百度下载安装)

打开软件 Discover Devices,可以找到局域网内的设备

在debug中可以具体调试,获取Token等功能

Requests中有各个方法的请求包样式,可供参考

接下来开始写具体用C#对接的内容

系列文章

程序目录的整理

想必C#的开发者都遇到过这个问题,引用的dll都放在根目录下,随着项目的日益增大,根目录下充满了各种各样的dll,非常的不美观。

如果能够把dll按照想要的目录来存放,那么系统就美观多了,以下是我常用的程序各文件的分布:

  • 【3rdLibs】
    • NLog.dll
    • Newtonsoft.Json.dll
    • ……
  • 【MyLibs】
  • 【Resources】
  • 【Images】
  • Excecutable.exe
  • Excecuteble.exe.config

网上有很多的文章述说这个,比如使用Assembly.Load,但是没有说明在程序中怎么使用,也没有给出具体的代码。这里我结合自己多年的实践经验,再把整个流程和方法详细叙述一遍,以便各位看官有个具体的体会。

系统搜索dll的目录以及顺序

CLR解析一个程序集会在一个根目录内进行搜索,整个探索过程又称Probing,这个根目录很显然就是当前包含当前程序集的目录。

AppDomainSetup这个类存储着探索目录的信息,其成员包括:ApplicationBasePrivateBinPath

程序搜索dll的顺序如下(区分强名称签名的和没有强名称签名的程序集):

没有做强名称签名的程序集:

  • 程序的根目录
  • 根目录下面,与被引用程序集同名的子目录
  • 根目录下面被明确定义为私有目录的子目录
  • 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
  • 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录

具有强名称签名的程序集:

  • 全局程序集缓存
  • 如果有定义codebase,则以codebase定义为准,如果codebase指定的路径找不到,则直接报告错误
  • 程序的根目录
  • 根目录下面,与被引用程序集同名的子目录
  • 根目录下面被明确定义为私有目录的子目录
  • 在目录中查找的时候,如果dll查找不到,则会尝试查找同名的exe
  • 如果程序集带有区域性,而不是语言中立的,则还会尝试查找以语言区域命名的子目录

如何让程序识别不同目录下的dll?

我们看到,上面的顺序无论是否有强名称签名看,都提到了一个名词“私有目录”

**方法一:配置App.config文件的privatePath**——【推荐】

这是最简单的方法,当然也有一定的局限性,就是没法对dll做控制,另外,无法解决第三方DllImprt中引入的程序集不在根目录下的问题,不过无论怎么说,这个都基本解决了问题。

配置如下,多个目录用;分隔

1
2
3
4
5
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="3rdLib;MyLibs;SubFolder\Sub.dll"/>
</assemblyBinding>
</runtime>

方法二:订阅程序集解析事件AssemblyResolve在代码中解析

应用程序集域中支持在程序集解析时的处理:AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;。通过这个事件,我们可以在程序集解析时,根据不同的程序集做不用的处理,比如加载x86的程序集还是64位的程序集,当然也就可以指定程序集目录了

这也正是Assembly.LoadAssembly.LoadFrom等方法的用武之地。

1
2
3
4
5
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
return Assembly.LoadFrom(Path.Combine(baseDirectory, "3rdLibs"));
}

方法三:在加载使用到dll的代码之前设置重置当前环境的目录

这个方法就是通过Environment.CurrentDirectory=customPath,这样,在调用dll方法时,因为目录已经切换到了
这是一个取巧的方法,不是很实用,要来回切换程序集目录,但是在某些情况下非常好用

如何处理[dllImport]中的程序集的加载

自己写dllImport

如果是自己写,那么久好控制了,可以直接指定相对的目录DllImport(3rdLibs\NLog.dll)。不过这种方法不一定可靠,在某些系统硬是加载不了,如果使用了dllImport还是,推荐下面的另外一种方法。

引用的C#的插件又使用了dllImport

这是很多文章都没有提及的:

因为无法更改路径,那么只能够使用上述特殊的方法,更改当前程序的路径

当然,还有更省事一点的做法,就是在系统环境中,增加一条记录,指向要加载的dll的所在目录。因为C++的代码中,Windows目录和Windows\System32目录以及环境变量设定的目录都是搜索路径之一。

这里提供怎么从C#中修改系统环境变量的代码:

1
2
3
static void AddEnvironmentPaths(IEnumerable<string> paths)
{
var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty };
1
string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths));
1
2
    Environment.SetEnvironmentVariable("PATH", newPath);
}

参考文章


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注马非码的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/marvin/欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。

一、AOP框架

        Encase 是C#编写开发的为.NET平台提供的AOP框架。Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式。这种部署方面(aspects)的方法帮助缺少经验的开发人员提高开发效率。

        NKalore是一款编程语言,它扩展了C#允许在.net平台使用AOP。NKalore的语法简单、直观,它的编译器是基于Mono C#编译器(MCS)。NKalore目前只能在命令行或#Develop内部使用。NKalore兼容公共语言规范CLS(Common Language Specification),它可以在任何.NET开发环境中使用,包括微软的Visual Studio .NET。

        PostSharp读取.NET字节模块,转换成对象模型。让插件分析和转换这个模型并写回到MSIL。PostSharp使开发程序分析应用程序容易得像分析代码规则和设计模式,它使程序开发的思想变革为面向方面软件开发(AOSD/AOD)思想。

        AspectDNG的目标是为.NET开发人员提供简单而功能强大的AOP-GAOP实现。它效仿java下的开源工具AspectJ 和 Spoon,成熟程度也很接近它们。

        RAIL(Runtime Assembly Instrumentation Library) 开源项目可以在C#程序集加载和运行前进行处理控制调整和重新构建。C#在CLR中,我们已经能够动态加载程序集并且获得程序集中的类和方 法,RAIL(Runtime Assembly Instrumentation Library)的出现填补了CLR处理过程中的一些空白。

        SetPoint是一款.NET框架下的全功能(full-featured)AOP引擎.它着重为称为语义切点(semantic pointcuts)的定义依赖RDF/OWL的使用.它的功能为一个IL-level,highly dynamic weaver&LENDL,一个引人注目的定义语言、、、、、、

        DotNetAOP为 CLR language提供AOP 框架基础属性。

        NAop是一个DotNet下的AOP框架。

        AspectSharp是DotNet下的免费AOP框架,它以Dynamic Proxies和XML作为配置文件。

二、Ajax框架

        Ajax.NET Professional (AjaxPro)是最先把AJAX技术在微软.NET环境下的实现的AJAX框架之一。它在客户端脚本之上创建代理类来调用服务器端的方法。

        MagicAjax.NET是一款在ASP.NET下创建Web页面提供AJAX技术的框架。它使开发人员很容易把AJAX整合到他们的页面而不需要替换ASP.NET控件或自己写javascript脚本代码。

        Anthem.NET是为ASP.NET开发环境提供的开源AJAX工具包,它可以运行于ASP.NET 1.1和2.0。

三、工作流(workflow)

        Workflow.Net是使用微软.Net技术基于wmfc标准的创建工作流引擎。

        NetBPM是JBpm移植到.net平台下的一款开源工作流软件。NetBpm可以很容易和.Net应用程序集成在一起,可以创建,执行和管理工作流程序。

        Bpm Tool支持将业务模型转换成软件模型。业务开发人员可以使用模型驱动的方法设计,实现,执行和跟踪业务流程。因此开发人员能够更容易的关注业务逻辑的变化。

四、文本编辑

        FCKeditor是一款功能强大的开源在线文本编辑器(DHTML editor),它使你在web上可以使用类似微软Word 的桌面文本编辑器的许多强大功能。它是轻量级且不必在客户端进行任何方式的安装。

        FreeTextBox 是一个基于 Internet Explorer 中 MSHTML 技术的 ASP.NET 开源服务器控件。这是一款优秀的自由软件(Free Software),我们可以轻松地将其嵌入到 Web Forms 中实现 HTML 内容的在线编辑,在新闻发布、博客写作、论坛社区等多种 Web 系统中都会有用途。

        VietPad是一个功能完整的跨平台的Java/.NET的Vietnamese Unicode开源文本编辑器。支持打开,编辑,打印,转换,排序,和保存基于文本的Unicode格式的Vietnamese文件。

        NetSpell是一款.NET框架下的开源拼写检查引擎。

        PPC_edit是一款应用在Pocket PC上的开源文本编辑器,它支持TXT, RTF, HTML, WordML, DocBook 和 ZIP格式的文件,屏幕上会显示国际标准的软键盘。

五、博客(Blog)

        NovaShare是一款Blog引擎,它使你创建基于交互式的web的新闻和论坛网站,很像WonkoSlice或Slashdot。管理员可以发布文章和发起投票,浏览者可以创建用户帐号,发表议论等等。

        dasBlog是从BlogX 网上日志引擎发展而来。像Trackback ,Pingback 一样增加许多附加的特征,有完整的Blogger/MovableType API支持,API注释,完整的Radio-style模板定制,支持Mail-To-Weblog/POP3的附件和内嵌图片,基于WEB的 DHTML,OPML,配置的编辑器。

        DotText是一个被使用了数百个blogs的强劲的blog引擎。这是一个N-tiered应用的例子。

        tBlogger是一个C#开发的完整的blog网站程序,使用XML配置。

六、系统构建

        .NETZ是一款免费开源工具,它可以压缩和打包微软 .NET 框架可执行文件(EXE, DLL)以使他们更小。更小的可执行文件占用的磁盘空间较少且因为读取文件时对磁盘的访问较少而使读取数度更快。它和PE(portable executable)打包工具不一样,.NETZ是使用 C# 编写的存粹的 .NET 解决方案。.NETZ可以用来打包几乎每一种 .NET 支持的语言编写的程序。.NETZ支持 .NET EXE 和 非共享(non-shared)的 DLL 文件。压缩过的程序能以相同的方式解压缩这些对最终用户是透明的。

        NAntContrib为NAnt提供定制任务的工具。

        Prebuild是XML驱动的一款跨平台pre-build工具,使开发人员很容易就可以为IDE和.NET开发工具生成项目或构建文件。它支持 Visual Studio .NET 2002, 2003, 2005, SharpDevelop, MonoDevelop 和 NAnt。

        BusyBeeBuilder是.NET平台下功能强大,易于使用,可扩展的开源构建自动操作工具。

        Draco.NET 是 Windows 服务应用程序。它的设计使其容易持续的集成新特性。Draco.NET监视你的源代码储存库。当探测到你的项目有变化时自动重新创建项目并把包含变化列表的创建结果发送到你的Email。

        Build Studio为软件的自动构件处理提供了一套完整的解决方案。

        CruiseControl.NET是.NET平台下的一款整合服务器。

        NAnt类似Apache项目下的Ant,是.Net下的开源构建工具。适用在自动编译.NET应用的场合,如.NET项目的每日构建(nightly build)。

七、图表制作

        ZedGraph是C#编写的.NET类库,提供了用户控件和web控件。它可以创建2D的线性图、条形图和饼图。它功能完整且有详细的功能自定 义,不过使用默认的选项就足够好用了。一款类似 PieChart, StackBar, LineChart的C#开源图表组件。

        NPlot是一款.NET下的开源图表类库.它值得称道的地方是优雅且灵活的API设计.NPlot包含了Windows Form控件, ASP.NET控件和一个创建Bitmap图片的类。还有一个可用的GTK#控件。

        XSCharting是C#开发的图表组件,提供了多种多样的图表选项。

        DaveChart是一个免费的DotNet类库。

        NChart 提供了很多值得应用在商业,教育等多个领域的2 D图表。

八、聊天系统

        Dot Net Chat server是基于DotNet框架开发的聊天服务器和客户端项目。

九、内容管理系统(CMS)

        Ludico是C#编写的居于ASP.NET 2.0的Portal/CMS系统。它的模块化设计是你可以按照你希望的使用或开发网站功能。它里面有高级的用户管理,一个所见即所的(WYSIWYG)的编辑器等。

        mojoPortal是一款C#开发的面相对象网站框架,它可以运行于Windows的ASP.NET 和GNU/Linux 或Mac OS X的Mono的平台上。

        Cuyahoga是C#开发的灵活的CMS / Portal 解决方案。它可以运行于Microsoft .NET 和Mono 平台,支持SQL Server, PostgreSQL或MySQL作为底层数据库。

        Umbraco是一款在.net平台下C#开发的开源内容管理系统,该系统效率,灵活,用户界面都不错。

        Kodai CMS是.NET平台下的一款功能齐全的内容管理系统。

        Rainbow项目是一款使用Microsoft’’s ASP.NET和C#技术开发的有丰富功能的开源内容管理系统。

        NkCMS是使用ASP.net和Sql server 2000开发的内容管理系统。

        Amplefile是一款内容管理系统,是.Net环境下的windows应用程序,使用了.Net remoting.

        Go.Kryo是一个用ASP.NET(C#).NET 实现的简单的内容管理系统,后台数据库使用Microsoft SQL Server 。

        ndCMS是 ASP.net (C#)下的一个内容管理系统。它提供了用户管理,文件管理,一个WYSIWYG编辑器,模板管理,拼写检查和内置的http压缩。ndCMS的目标是提供一个简单而快速的方式部署.Net站点以节省你的时间和金钱。

十、代码覆盖(Code Coverage)

        NCover是.NET框架下的C#版本代码覆盖分析工具。NCover可以对程序进行 line-by-line 的代码覆盖统计。

十一、论坛系统

        YetAnotherForum可以作为ASP.NET开发的网站的论坛或是留言板。它使用MSSQL作为底层数据库。

十二、开发工具(IDE)

        SharpDevelop是一个DotNet平台下的免费开发工具,支持C#和VB.NET。

        MonoDevelop是可以支持C#和其它符合.Net规范的开发语言的IDE。

        C# Studio是 C#/Mono/GTK# 开发者的一个简单的IDE。

        izfree是一套套免费的工具用于帮助创建使用Microsoft’’’’s Windows Installer 技术的安装程序。使用izfree你可以为你的应用程序制作强劲的安装程序。

        Windows Installer XML (WiX)可以重XML源文件创建Windows程序安装包的工具集。它支持命令行方式,开发人员可以把结合它来创建MSI和MSM安装包一个可以和商业软件安装产品相比的开源打包工具。

        Spring.net是从java的Spring Framework移植过来的。java的Spring包含了许多功能和特性,在当前的Spring.net都有提供。Spring.net最初发布的版本包含了一个很有特色的IoC容器。

        Castle是一组应用开发的工具,内含一个简单的IoC容器。

        StructureMap是.NET环境下的一个轻量级依赖注入工具,StructureMap也是一个灵活的、可扩展的通用“插件”机制的.NE

        BugNet是一款C#编写的基于Web的开源Bug跟踪系统。

        BTsys是一款轻量级的开源桌面Bug跟踪系统,是C#和ADO.NET实现。一个Bug记录和跟踪的解决方案,支持文件附件和注释历史。

        BugTracker.NET是使用ASP.NET 和 C#开发的基于web的开源Bug跟踪系统。需要SQL Server 或 MSDE支持。它安装简单,容易使用,功能强大。

        BugBye是一款ASP.NET和C#开发的基于web的Bug跟踪系统。

        log4net是一个可以帮助程序员把日志信息输出到各种不同目标的.net类库。它可以容易的加载到开发项目中,实现程序调试和运行的时候的日志 信息输出,提供了比.net自己提供的debug类和trace类的功能更多。log4net是从java下有卓越表现的log4j移植过来的。它是 apache基金资助的项目的一部分。

        NLog是C#编写的开源日志类库,它的设计思想是使其简单而灵活。NLog让你处理诊断的日志消息,用相关信息扩充消息,依照你的选择格式化日志消息和把日志消息输出到一个或多个目的地。

        LogThis是为.NET应用程序提供的一款C#开源日志框架,它可以嵌入到应用程序之中。

        AppLog是一个简单的应用日志工具。它使用C#开发,且使用ByteFX MySQL 数据访问库.。

        C#开发的可扩展日志工具,有高级消息队列支持,可以异步使用。

        CSharp Logger是apache继log4net项目后设计的又一个日志工具。它用来向Windows的事件日志写入debug、info、warn和error四个等级的信息。

        .NET FTP Client是C#编写的开源类库。

        .NET Telnet是微软.NET Framework下的C#开发的开源telnet类库。它的灵感来至Java Telnet Application。

        metro这个项目是C#编写的类库,它提供了一套丰富的类使开发IP version 4, TCP, UDP and ICMP等工作更容易。它包含了有很有用的工具如包嗅探器,网络分析工具例如路由跟踪,ping等。

        LJ.NET是LiveJournal站点的客户端。它为LJ在线日志服务提供了简单而强大的用户接口。

NET VNC Viewer 是一款完全用C#开发的开源VNC观察器。它兼容Smartphones, Pocket PC和Windows的电脑(.NET CF or .NET Framework)。它比起其它观察器的优点是可以在Pocket PC上全屏显示而且可以旋转屏幕。

        GVDownloader允许你从google videos, metacafe, putfile, youtube, break.com 和更多的地方快速下载内含的视频和多媒体。它的包含一个强劲IE插件和位于你系统托盘的独立程序。

        DotNetOpenMail能够使你在微软.net框架开发的asp.net, WinForm应用程序发送Email。它是C#编写的开源组件,它不需要使用System.Web.Mail类库就可以容易的创建带附件HTML和 Plain-text的Email。程序员不需要知道很多相关的细节就可以使用不同的字符集或不同的MINE编码来创建 multipart/alternative,multipart/related和multipart/mixed的MIME消息。

        DotMSN是一款独立的开源类库,它不需要和官方的MSN Messenger交互,因此不必安装MSN Messenger就可以使用DotMSN和MSN Messenger服务通信.DotMSN是C#编写的,所以.NET环境支持的语言都能够使用.DotMSN类库使用简单而且实现方便。它灵活,坚固, 轻量级利于整合到任何应用系统.使用DotMSN的应用系统能实现从创建消息机器人到自定义客户端等各种不同的功能.如果你的应用程序需要和 Messenger服务通信,DotMSN是一个不错的工具.

        SharpSSH使用C#实现了SSH2协议,它支持SSH, SCP 和 SFTP.

        OpenPOP.NET一组和POP Servers通信的.NET类库。

        IceChat是为连接多样的IRC Servers设计的Internet Relay Chat Client。

        lphant是为edonkey/emule开发的开源客户端程序。

        OpenSmtp.net 是 C# 开发的开源SMTP组件。它不依赖.NET Framework 的System.Web.Mail 包中的类。允许开发人员使用不同于MS SMTP的SMTP 服务器且提供了web service而可以通过HTTP发送email。

十八、网络服务器

ODC(#)H是一款C#编写的开源hub software,它功能强大,消耗的资源少且支持插件功能。

Dawn of Light (DOL)是第一个开源的Dark Age of Camelot (DAOC)游戏服务器,你可以创建自己的服务器且自己设计游戏内容。DOL使用.Net Framework框架的C#编写,使用XML和MySQL存储数据,支持Windows和Mono平台。

NeatUpload可以让ASP.NET开发人员把磁盘上的文件通过流的方式上传并且可以监视上传进度。 它是开源的且在 Mono’’s XSP/mod_mono和微软的ASP.NET下运行得一样好。包含2个自定义控件:*用户可以通过INPUTFILE选择一个要上传的文件。

ProgressBar可以用进度条方式或弹出窗方式显示上传进度。当JavaScript可用时ProgressBar是用类似AJAX的无刷新方式展现,但也允许JavaScript不可用的用户见到上传进度。

NMail ,C#开发的开源程序,提供SMTP 客户端和服务器, POP3 和 IMAP4 服务器。

CSharp Email Server   C# Email Server是 Java Email Server (JES) 的C#版本。

十九、PDF类库

PDFsharp是一款可以让.NET框架支持的任何语言很容易的创建PDF文件的类库。

ASP.NET FO PDF 是一款C#编写类似于ASP.NET服务器控件的控件。它接受DataTable 和一些其它参数来创建XSL FO,并使用NFOP (Apache FOP Port in J#) PDF Formatter来绘制一个类似PDF Report 的DataGrid 。今后将会增加更多的标签来可以生成XSL FO 。

Report.NET 开源类库包含了生成精确PDF文档的类。它是.NET平台下的C#编写的,可以帮助你创建简单的灵活的PDF文件。你可以从任何ADO.NET的 DataSet取得数据来创建PDF文档。ASP.NET可以用Report.NET来创建动态的PDF响应页面。

SharpPDF是可以用来简单的创建PDF文件的C#类库。它创建的文件百分白兼容PDF格式。

二十、持久层框架

NHibernate是一个面向.NET环境的针对关系型数据库的对象持久化类库。 NHibernate来源于非常优秀的基于Java的Hibernate关系型持久化工具。 NHibernate从数据库底层来持久化你的.Net对象到关系型数据库。NHibernate为你处理这些,你不用自己写SQL去数据库存取对象。你 的代码仅仅和对象关联,NHibernat自动产生SQL语句,并确保对象提交到正确的表和字段中去.大量减少开发时人工使用SQL和ADO.NET处理 数据的时间. NHibernate可以帮助你消除或者包装那些针对特定数据库的SQL代码,并且帮你把结果集从表格式的表示形式转换到一系列的对象去。因此对于那些在 基于.NET的中间层的应用中,它们实现面向对象的业务模型和商业逻辑的应用,NHibernate是最有用的。

FileHelpers Library是一款C#编写的开源 .NET 类库。它使用简单,很容易就可以从固定长度文件或界定记录(CSV)读/写数据。它也支持从不同的数据存储格式(Excel, Access, SqlServer)导入/导出数据。

Websharp是国人开源的一款开源持久层框架,它的目标是设计一个基于.Net的通用的应用软件系统的 框架,以简化基于.Net平台的企业应用软件的开发。目前,Websharp关注于企业应用软件的以下几个方面:1、数据库访问 2、 O/R 映射 3、 AOP 4、 分布式访问

ObjectBroker是.NET平台下的一款开源O/R映射框架。它支持对象缓存,1:1, 1:n 和 m:n的关联映射等特性。

Gentle.NET是一款开源的与关系数据库(RDBMS)无关的对象持久层框架,可以自动生成SQL和对象结构。它拥有一个SQL工厂用来创建自定义查询、DataView构建助手和卓越的性能和完善的文档。

Ubik是C# 2.0下的ORM持久层框架,当前是WinForms应用程序开发提供的.它支持OPath的子集而可以进行面向对象查询,且包含一个网络事件系统.

NDal是一个数据提取层(DAL)框架,它可以运行在.NET和Mono环境下。

Persist.NET是C#编写的一款完整的持久层框架。

ObjectBroker是.NET平台下的数据库对象/关系映射(O/R Mapping)框架。

iBATIS.NET帮助你的应用系统创建更好的持久层框架。

Advanced Data Provider是为ADO.NET提供的动态数据提供者。可以让应用程序透明的访问不同的ADO.NET 数据提供者。

OJB.NET是一款.NET平台下的对象/关系映射(O/R Mapping)工具。

二十一、门户系统

OmniPortal基于一种web-portal-kernel(Web门户核心)的思想而构建的开源基 础框架,它解决了安全控制、主题和本地化等。你可以基于 OmniPortal 来更容易的创建任意的Web应用系统(例如 ERP, CMS, CRM)。它支持Mono 和.Net 。对于Web开发人员来说,OmniPortal为他们提供了一款优秀的二次开发基础类库。

SharpNuke.NET是一款C#开发的基于 ASP.NET 的开源 Portal/CMS。最初是从流行的VB.NET开发的web portal系统DotNetNuke移植过来的的。以后的版本将会兼容 Windows和 Linux Mono。

Personal .NET Portal是一个基于Web的个人门户系统。它试图提供一种简单的方式建立个人网站,页面使用标签和模板创建。

My Community Portal提供了一个统一的Internet门户,作为论坛,群组,聊天,你自己的Email,搜索引擎,网络目录,个人主页,投票还有更多的功能入口。

二十二、剖析工具(Profilers)

Prof-It是一款独特易用的C#程序剖析工具。当保持对一个最小量的源代码的探测时,它为每一条语句测量执行频率。

NProf不仅仅是成熟的剖析程序,也是一套可以用来创建其它功能齐全的剖析程序的完整API,以及用可视化的工具扩展的默认GUI。

NProfiler,.NET平台下的一个应用程序剖析工具。

二十三、项目管理

SharpForge支持协作多个软件项目的开发和管理,它为你的团队提供类似 SourceForge 和 CodePlex的功能。SharpForge是C#开发的.NET 2.0开源项目。

User Story.NET是一个Extreme Programming 项目。

二十四、RSS和RDF工具

Rss Bandit是C#开发的开源RSS阅读器,它内建多国语系,支持Atom 1.0以及其他各种的RSS feed格式。它画面漂亮,拥有分页浏览功能,软件细部设置弹性度高。它的操作画面直观简单易懂,有一个很好的Command模式和ListViewEx 来使用,可让使用者很快的上手。

iPodder.NET是一款C#编写的开源媒体收集器,它能自动帮你从互联网上下载音乐,能帮你轻松地从 成千上万的音乐中选择你喜欢的。用它设置好订阅RSS feeds后,只要节目一有更新,它就自动下载了,你不必大量的浪费时间自己手工下载。它还整合了iTunes,创建播放列表和iPod同步功能,你可以 用Apple iTunes或Media Player播放,也可以把音乐导入到你的iPod或其他MP3播放器中。

FeedExpress主要是让你订阅一些RSS/RDF资源,知名的如FeedExpress里的Subscriptions。它和Outlook Express让你订阅一些新闻组是一样的。

RSS.NET是一款操作RSS feeds的开源.NET类库。它为解析和编写RSS feeds提供了一个可重用的对象模型。它完全兼容RSS 0.90, 0.91, 0.92, 和 2.0.1等版本。

GtkSharpRSS是C#开发的开源RSS和RDF客户端,它用于 Mono, Gtk#, 和 RSS.NET。它也可以通过内嵌于Mozilla的窗口来显示内容。

Atom.NET是一款完全使用C#开发的开源类库,它的目标是提供便捷的方法来操作Arom Feeds。它提供一个对象模型来写入和解析Atom Feeds。它只兼容0.3Atom规范,可以运行于微软.NET 1.x,Mono 0.29 或更高版本。

NRss是为微软.NET框架和Mono下提供的开源类库,它读取RSS Feeds元素然后以树型结构来展现对象关系。

Aggie是一个新闻收集器,它是桌面应用程序,可以下载最新的新闻并通过web页面的形式呈现。

mail2rss是.NET下的 web service,它在你请求时检查你的电子信箱并返回包含电子信箱里的消息的 RSS feed。它是完全用C#开发的开源项目,没用使用任何第三方控件。

NxBRE是.NET平台下的一款开源轻量级的业务规则引擎(aka Rule Based Engine),它由正向串行的推理引擎(forward-chaining inference engine)和XML驱动流控制引擎(XML-driven flow control engine)组成.它支持 RuleML 0.9 Naf Datalog 和 Visio 2003 建模。

SRE (Simple Rule Engine)是.NET下的一款轻量级的开源正向串行的推理规则引擎(forward chaining inference rule engine)。它容易理解,可以解决复杂的问题。

DotLucene,Lucene.Net开源项目转向商业化后,DotLucene以Lucene.Net作为基础继续进行开发。

Porc是为CVS和项目管理工具提供了完整的图形化界面,它在CVS的基础上增加了一些有趣的功能。它是.NET平台下用C#编写的。

Sharpcvslib是C#语言编写的CVS客户端API。当前它为公共CVS命令提供了核心函数。

NetCvsLib是为.NET平台下C#开发的一个CVS客户端。它作为一个单独的程序集而实现,因而可以很容易的集成到其它项目中去。

SqlBuddy是C#编写的一款用于Microsoft SQL Server和MSDE的开源工具,使用它可以很容易的编写SQL脚本。SqlBuddy提供的功能和查询分析器的目的有些微不同,它倾向于帮助使用者编写SQL。

QueryCommander是一款开源的Sql editor,模拟微软的查询分析器,Visual Studio的环境类型。QueryCommander的特点包括类似IntelliSense功能,自动注释,xml文档模型,xml2data等。

SQL Buddy是一款有特色的免费MSDE / Sql Server IDE,它是用.NET框架的C#编写。

Database Commander是Windows操作系统下的一款用户界面友好的数据库管理工具。

NVelocity 是一个以 .NET 为基础的模板引擎。它允许任何人通过简单而强大的模板语言来定义对象。

NUnit一款单元测试框架,它可以应用于遵循.NET框架标准的所有语言下。NUnit最初是从JUnit移植过来的。NUnit完全使用C#编写且设计时考虑了多数.NET语言的特性,例如自定义属性和其它反射特性。

NMock是一款.NET平台下的基于动态代理的Mock模拟对象类库,用于C#开发。Mock对象使测试 更简单,它测试单个组件或单个类时不需要其它所有组件真正实现。也就是说我们可以仅仅只是测试一个类,比起测试一个完整的对象关系树更容易查清楚Bug。 Mock对象一般用于以测试为驱动的开发当中。

TestDriven.NET是一款和Visual Studio .NET无缝结合的测试插件。它可以和NUnit, MbUnit 和 Team System紧密结合。

Rhino.Mocks源于EasyMock.Net,它试图提供一种更简单的方法去创建和使用mock objects且使你得到更好的重构支持。它结合了EasyMock.Net和NMock的方式。

Dot NetUnit实现了XUnit 测试框架,是.NET平台下的单元测试组件。

EasyMock.NET是由Java实现的EasyMock到.net平台的一个移植版本的一组类库。它提供了一个简单的方法用来模拟接口和远程对象。

dotunit是JUnit移植到微软.net平台的测试框架。它可以实现自动化测试。

NUnitForms是NUnit的扩展。它为Windows Forms应用程序提供单元测试和压力测试,可以容易的用它为你的Windows Forms类进行自动化测试。

NStruts 是java下大名鼎鼎的struts开源MVC框架的ASP.NET版本,是jakarta struts框架的一部分。

Maverick.NET是从Maverick移植来的.NET版本,是一款开源的 Web MVC框架.它专注于MVC逻辑,它支持丰富的模板和转换技术。

MaverickLite是ASP.Net下的一款简单的开源表现层框架。它在建立在Maverick.Net framework基础上。它使用front controller的方式来处理请求。

Ingenious MVC ,基于.NET 2.0的构MVC结构的应用程序,它包含即开即用(out-of-the-box)的特性支持ASP.NET和Windows Forms应用程序.它也可以在大多数方法中扩展和定制.

Websharp是.NET平台下的一款轻量级应用框架。

GmailerXP是为Google的Gmail服务提供了全套的工具开源程序。在线所能做的GmailerXP都允许用户做,它也整合了你任何时候在Gmail需要的其它一些程序的特点(GML, Gmailto,等)。

RemoteCalendars是C#开发的为Outlook 2003/2007提供的一款开源 COM-.NET 插件。安装完这款插件后,Outlook的用都可以用Outlook 2003/2007订阅、重新读取和删除远程iCalendar(RFC 2445)。

SharpWebMail是一款C#编写的ASP.NET下的EMail客户端。它通过POP3服务器接收邮件,通过SMTP服务器发送邮件。它的配置很简单(只需要在web.config 里面做几个设置)。

NUnitAsp是一款自动测试 ASP.NET页面的开源工具. 它是从NUnit扩展来的。

Perspective是一款C#开源Wiki引擎,它使用户可以很容易的合作进行编辑,链接和共享Web页面,它里面有安全和分级的管理环境。Perspective居于奇妙的Wiki思想,包含了其它一些有前途且效果好的特性。

DevHawk Wiki是一款.NET平台下由C#实现的开源Wiki引擎。

ProntoWiki是使用VWD (Visual Web Developer)编写的C#开源wiki引擎,ASP.NET 2.0 和 SQLExpress 2005作为后台。Wiki引擎速度快且容易使用,提供了和 rich text editor 一样的文本标记( text markup )功能。它可以自动通过web接口接收图片和文件作为附件。另外,ProntoWiki的特点还有页面可以在贴出或更新之前预览,基于角色的用户认证,使 用 web parts 自定义外观和布局,历史跟踪,和功能查询。

FlexWiki是一款使用.NET技术(C#和ASP.NET)编写实现的开源多人协作的写作工具。

Thufir提供了字处理和文档链接功能。它类似Wiki-Wiki ,且它具有“所见即所得”的界面。它还可以通过和Word交互来利用Word的拼写检查功能。

ODF Add-in for Microsoft Word是一款开源的XML转换器,它提供的工具为在 Open XML Formats 和 Open Document Format(ODF)之间转换提供了桥梁。作为第一款这类组件,ODF Add-in for Microsoft Word 2007使你可以在Word里打开和保存ODF文档。

NDigester是Jakarta Commons-Digester 工具的.NET实现版本。提供了完整的对XML文档的处理控制。

SAX.NET是用来操作XML的简单的API。它是从最初在java上被广泛使用的API移植来的。

FreeImage是为开发人员提供的开源类库项目,它支持多种流行的图片格式(PNG, JPEG, TIFF, BMP等).FreeImage简单,易用,快速,多线程安全,兼容所以32位的windows并且是跨平台的(Linux和Mac OS X).

TVGuide是一款显示通过简单而清晰的接口下载的TV列表的Windows程序。

DirectShow.NET Library 这个类库的目的是使你可以从.NET应用程序内部调用Microsoft’’’’s DirectShow的功能。它支持Visual Basic .NET 和 C#,理论上应该是可以支持任何.NET语言。

iTunes Agent是和iTunes一起使用的C#开源MP3播放器。它可以使得在播放MP3时,播放列表与MP3自动播放器同步。

MeGUI是一款基于ISO MPEG-4的全面的GUI解决方案。它可以支持使用MPEG-4 ASP (XviD & libavcodec MPEG-4)标准的mencoder,使用MPEG-4 AVC (x264) 标准的x264.exe 或 mencoder,使用(HE)AAC audio标准的BeSweet、Nero AAC encoder和使用MP4 muxing标准的mp4box。

OpenImageManager是一款C#开发的新的开源图片管理器。它基于非常流行的图片查看器ACDSee(TM)的思想设计,它的功能有缩略图生成,HTML相册生成器,支持10种语言等等。

abrViewer.NET是C#编写的可以批量浏览和输出Adobe Photoshop的笔刷(.abr files)的开源工具。

MeWiG是使用.NET框架开发的Windows下的开源MEncoder(MPlayer的一个组成部分)。你能够转换成几乎任何视频资源,无需要命令行操作。

VTK.NET是.NET包装的Visualization Toolkit。你可以使用它来编写C#、C++、Visual Basic 或 J#的Windows Forms应用程序。它包含在设计时可拖拽的Windows Forms控件。

SubtitleCreator使你可以为你的DVD创建对白字幕。它拥有先进的同步特性,DVD预览,和一个简单的所见即所得(WYSIWYG)的编辑器。当然你也能够修改现有DVD的对白字幕的定位和颜色。

Paint.NET是在Windows下使用C#编写的一款出色的开源图像编辑器。

MediaPortal试图帮你把电脑变成一个高级的Multi-Media Center / HTPC。它允许你听你喜欢的音乐&收音机,看你的video和DVD,观看,确定和记录实况电视等其它功能。MediaPortal是开源的系 统,你可以完全免费的获得它。任何人都可以帮组开发MediaPortal 或者根据自己的需要把Media Portal 进行调整。

Exult是可以在现在的操作系统上面运行Ultima7的游戏引擎,它提供了一个地图编辑器和其它的一些工具让你制作属于你自己的模型和游戏.

Ch2r是第一款为光晕2(Halo 2)提供的开源模型编辑器。

这是一款从在Palm平台上流行的Space Trader游戏移植的Windows版本。

EMU7800是C#编写的一款Atari 2600/7800模拟器。

BooGame是面相对象的 CLS-compliant,开源2D游戏引擎框架。它提供了高级的硬件加速video,audio,keyboard,mouse,内嵌脚本和许多图片格式。它使用C#开发的,目标是支持.NET 1.1, 2.0 和 Mono 平台。

RealmForge这款开源的.NET 3D游戏引擎前身是Visual3D.NET,它是一套使用C#、 .NET 2.0和XNA进行可视化设计和开发3D游戏、模拟软件和交互式环境的框架和工具集。

Pocket Sudoku是 Pocket PC (Windows Mobile)上的数独(Sudoku)游戏。它可以有五种不同等级的难度来创建数独(Sudoku)迷题,解答正确任何迷题,都会用铅笔标记。

CsGL在.Net框架下为OpenGL 1.1 - 1.4和许多扩展的支持提供支持.

NDoc 可以将 C#.NET 编译生成的程序集和对应的 /doc XML 文档,自动转换成如 .NET Framework SDK 类库文档或者 MSDN Library 在线 .NET 类库文档形式的代码文档,让您快速拥有专业级的类库API 文档。

Sharp Vector Graphics (SVG#)是.Net框架下C#开发的文档对象模型。它帮助你在.Net平台上进行SVG开发。

IronPython是.NET平台上的Python的CodeName。它拥有一个完全动态的交互式解释 器,可以支持所有的.NET类型库,并且集成了.NET Framework的诸多优点,甚至可以用Python的类来扩展.NET类。IronPython遵从ECMA的CLI标准(ECMA-335),可以 运行在Microsoft .NET Framework和MONO上。

SharpMap是一款易于使用的地图渲染器,它可以为Web和Windows应用程序渲染GIS数据。SharpMap是使用C#编写,基于.NET 2.0框架上开发的开源项目。

monoGIS将成为Mono平台下的开源完整GIS。已经发布了internet mapserver,OGC WMS实现和一些工具像空间格式转换。

NASA World Wind 是C#开发的个人电脑上的开源的3D图形虚拟地球系统。它结合了美国国家航空航天局(NASA)从卫星拍摄的图像,这些图像应用于Blue Marble, Landsat 7, SRTM, MODIS 以及其它更多的地方。

DockPanel Suite是一款开源的.Net Windows Forms开发环境下的停靠控件(docking)类库。它模仿Visual Studio .Net的效果开发的。

wx.NET是基于.NET通用语言基础结构 (CLI)的开源项目。它致力于开发一个基于.NET的GUI库,它按照wxWidgets的类层次用C#编写,可以在多种CLI下执行,包括.NET,Mono,和DotGNU Portable.NET。

MMC .NET类库是为Windows的MMC+Snapin开发提供支持而包装的一套开源类库。

DPAToolkit(Design Pattern Automation Toolkit)是应用设计模式设计应用程序的开源工具包,它有便捷的代码生成,反向工程功能。拖拽(Drag and Drop)方式便捷的创建UML类图。支持为代码生成,反向工程编写自定义插件。

mjbWorld 是一款开源的3D编辑器,它允许用户通过图形界面的选择和拖拽方式来编辑图形。它基于和应用X3D与VRML标准。该软件目的用于利用建立开放免费程序建 立3D世界。同时它也可以用于其他免费开放软件程序之间进行数据交换。mjbWorld有Java, C# 和C++ /Qt等版本。

MonoCalendar是.NET平台下的开源日历程序,它的目的是为Mac平台之外的其它平台提供一款类似iCal的日历程序。

Data Tier Generator是一款微软 .NET平台下的开源项目,它为访问SQL Server 2000而生成存储过程的SQL Server 2000脚本和C#类。这些存储过程包含了Insert, Update, Delete, Select, 根据PK的Select, 根据FK的Select的SQL脚本。

Finisar.SQLite是.NET框架下的一款为存取SQLite-Databases提供的ADO.NET Data Provider。

Math.NET的目标是为提供一款自身包含清晰框架的符号运算和数学运算/科学运算,它是C#开发的开源类库。Math.NET含了一个支持线性代数的解析器,分析复杂微分,解方程等等功能。

Ascii Generator dotNET (ascgen2)是一款把图片转换成高质量的ASCII字符组合(ASCII art - text)。第一个版本支持变形和粗体字体,和实时调节输出。

GPSProxy是.NET Compact Framework下的开源程序。它通过COM口在Pocket PC之间拷贝GPS数据,可以把GPS数据格式从NMEA 0183转换成Garmin GPS。它是基于.NET Fra的Windows版本。

FOP C# Port这是Apache居于java的 XSL-FO 格式转换器的C#版本。它采用并扩展了XSL-FO标准使FOPs在可以嵌入.NET CLR管理代码应用。

作者:心存善念
本文地址:https://www.cnblogs.com/xcsn/p/4678426.html
欢迎转载,请在明显位置给出出处及链接。

C#总结(六)EventBus事件总线的使用-自己实现事件总线 - 章为忠 - 博客园

Excerpt

在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外


在C#中,我们可以在一个类中定义自己的事件,而其他的类可以订阅该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外,注册其他类的事件会使得类紧耦合。事件总线便可以用来解耦并重复利用应用中的逻辑。

事件总线带来的好处和引入的问题

好处比较明显,就是独立出一个发布订阅模块,调用者可以通过使用这个模块,屏蔽一些线程切换问题,简单地实现发布订阅功能。

坏处可能比较隐晦,但这些需要足够引起我们的重视

  • 大量的滥用,将导致逻辑的分散,出现问题后很难定位。
  • 没办法实现强类型,在编译的时候就发现问题。
  • 代码可读性有些问题,IDE无法识别这些协议,对IDE不友好。

总得来说,如果项目里面有大量的事件交互,那么还是可以通过EventBus来实现,否则还是推荐自己在模块内部实现观察者模式。

示例代码

所以今天介绍一个简单的事件总线,它是事件发布订阅模式的实现,让我们能在领域驱动设计(DDD)中以事件的弱引用本质对我们的模块和领域边界很好的解耦设计。

目前,所有的源代码已经提交到github 上,地址:https://github.com/weizhong1988/Weiz.EventBus

 程序目录结构如下:

事件总线

事件总线是被所有触发并处理事件的其他类共享的单例对象。要使用事件总线,首先应该获得它的一个引用。下面有两种方法来处理:

订阅事件

触发事件之前,应该先要定义该事件。EventBus为我们提供了Subscribe 方法来订阅事件:

复制代码

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
<span>public</span> <span>void</span> Subscribe&lt;TEvent&gt;(IEventHandler&lt;TEvent&gt; eventHandler) <span>where</span><span> TEvent : IEvent
{
</span><span>//</span><span>同步锁</span>
<span>lock</span><span> (_syncObject)
{
</span><span>//</span><span>获取领域模型的类型</span>
<span>var</span> eventType = <span>typeof</span><span>(TEvent);
</span><span>//</span><span>如果此领域类型在事件总线中已注册过</span>
<span>if</span><span> (_dicEventHandler.ContainsKey(eventType))
{
</span><span>var</span> handlers =<span> _dicEventHandler[eventType];
</span><span>if</span> (handlers != <span>null</span><span>)
{
handlers.Add(eventHandler);
}
</span><span>else</span><span>
{
handlers </span>= <span>new</span> List&lt;<span>object</span>&gt;<span>
{
eventHandler
};
}
}
</span><span>else</span><span>
{
_dicEventHandler.Add(eventType, </span><span>new</span> List&lt;<span>object</span>&gt;<span> { eventHandler });
}
}
}</span>

复制代码

所以的事件都集成自IEvent,该类包含了类处理事件需要的属性。

复制代码

1
2
3
4
5
6
7
<span>var</span> sendEmailHandler = <span>new</span><span> UserAddedEventHandlerSendEmail();
</span><span>var</span> sendMessageHandler = <span>new</span><span> UserAddedEventHandlerSendMessage();
</span><span>var</span> sendRedbagsHandler = <span>new</span><span> UserAddedEventHandlerSendRedbags();
Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendEmailHandler);
Weiz.EventBus.Core.EventBus.Instance.Subscribe(sendMessageHandler);
//</span><span>Weiz.EventBus.Core.EventBus.Instance.Subscribe&lt;UserGeneratorEvent&gt;(sendRedbagsHandler);</span>
Weiz.EventBus.Core.EventBus.Instance.Subscribe&lt;OrderGeneratorEvent&gt;(sendRedbagsHandler);

复制代码

发布事件

对于事件源,则可以通过Publish 方法发布事件。触发一个事件很简单,如下所示:

复制代码

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
<span>     public</span> <span>void</span> Publish&lt;TEvent&gt;(TEvent tEvent, Action&lt;TEvent, <span>bool</span>, Exception&gt; callback) <span>where</span><span> TEvent : IEvent
{
</span><span>var</span> eventType = <span>typeof</span><span>(TEvent);
</span><span>if</span> (_dicEventHandler.ContainsKey(eventType) &amp;&amp; _dicEventHandler[eventType] != <span>null</span> &amp;&amp;<span>
_dicEventHandler[eventType].Count </span>&gt; <span>0</span><span>)
{
</span><span>var</span> handlers =<span> _dicEventHandler[eventType];
</span><span>try</span><span>
{
</span><span>foreach</span> (<span>var</span> handler <span>in</span><span> handlers)
{
</span><span>var</span> eventHandler = handler <span>as</span> IEventHandler&lt;TEvent&gt;<span>;
eventHandler.Handle(tEvent);
callback(tEvent, </span><span>true</span>, <span>null</span><span>);
}
}
</span><span>catch</span><span> (Exception ex)
{
callback(tEvent, </span><span>false</span><span>, ex);
}
}
</span><span>else</span><span>
{
callback(tEvent, </span><span>false</span>, <span>null</span><span>);
}
}</span>

复制代码

下面是发布事件的调用:

1
2
3
4
5
<span>var</span> orderGeneratorEvent = <span>new</span> OrderGeneratorEvent { OrderId =<span> Guid.NewGuid() };

System.Console.WriteLine(</span><span>"</span><span>{0}下单成功</span><span>"</span><span>, orderGeneratorEvent.OrderId);

Weiz.EventBus.Core.EventBus.Instance.Publish(orderGeneratorEvent, CallBack);</span>

定义处理事件

要处理一个事件,应该要实现IEventHandler接口,如下所示:

复制代码

1
2
3
4
5
6
7
8
9
10
11
<span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> send email
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>public</span> <span>class</span> UserAddedEventHandlerSendEmail : IEventHandler&lt;UserGeneratorEvent&gt;<span>
{

</span><span>public</span> <span>void</span><span> Handle(UserGeneratorEvent tEvent)
{
System.Console.WriteLine(</span><span>string</span>.Format(<span>"</span><span>{0}的邮件已发送</span><span>"</span><span>, tEvent.UserId));
}
}</span>

复制代码

处理多事件

在一个单一的处理句柄中,可以处理多个事件。这时,你应该为每个事件实现IEventHandler。比如:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> red bags.
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>public</span> <span>class</span> UserAddedEventHandlerSendRedbags : IEventHandler&lt;UserGeneratorEvent&gt;,IEventHandler&lt;OrderGeneratorEvent&gt;<span>
{
</span><span>public</span> <span>void</span><span> Handle(OrderGeneratorEvent tEvent)
{
System.Console.WriteLine(</span><span>string</span>.Format(<span>"</span><span>{0}的下单红包已发送</span><span>"</span><span>, tEvent.OrderId));
}

</span><span>public</span> <span>void</span><span> Handle(UserGeneratorEvent tEvent)
{
System.Console.WriteLine(</span><span>string</span>.Format(<span>"</span><span>{0}的注册红包已发送</span><span>"</span><span>, tEvent.UserId));
}
}</span>

复制代码

最后

以上,就把事件总线介绍完了,完整的代码,请到github 上下载,这个只是EventBus 的简单实现,各位可以根据自己的实际场景和需求,优化修改。

前言

算法这个东西其实在开发中很少用到,特别是web开发中,但是算法也很重要,因为任何的程序,任何的软件,都是由很多的算法和数据结构组成的。但是这不意味着算法对于每个软件设计人员的实际工作都是很重要的。每个项目特点和需求特殊也导致算法运用场景上不同。但是个人觉得算法运用的好的话会给自己在程序设计的时候提供比较好的思路。下面就对一些排序算法小结一下,就当做自己的一个笔记吧。

插入排序

1.简介

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

2.算法描述

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2~5
如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的数目。该算法可以认为是插入排序的一个变种,称为二分查找排序。

3.使用插入排序为一列数字进行排序的过程 

最差时间复杂度 O(n^{2})

最优时间复杂度 O(n)

平均时间复杂度O(n^{2})

4.C#实现

复制代码

    /// <summary>
    /// 插入排序 /// </summary>
    public class InsertionSorter
    { public void Sort(int\[\] list)
        { for (int i = 1; i < list.Length; ++i)
            { int t = list\[i\]; int j = i; while ((j > 0) && (list\[j - 1\] > t))
                {
                    list\[j\] \= list\[j - 1\]; \--j;
                }
                list\[j\] \= t;
            }

        }
    }

复制代码

数组

int[] iArrary = new int[] { 1, 5, 3, 6, 10, 55, 9, 2, 87, 12, 34, 75, 33, 47 };

希尔排序

1.简介

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

2.算法实现

原始的算法实现在最坏的情况下需要进行O(n2)的比较和交换。V. Pratt的书[1] 对算法进行了少量修改,可以使得性能提升至O(n log2 n)。这比最好的比较算法的O(n log n)要差一些。
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n2)的排序(冒泡排序或插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
一个更好理解的希尔排序实现:将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size而不是i++)。

3.排序过程

最差时间复杂度 根据步长串行的不同而不同。O(n\log^2 n)

最优时间复杂度 O(n)

平均时间复杂度  根据步长串行的不同而不同。

4.C#实现

复制代码

    /// <summary>
    /// 希尔排序 /// </summary>
    public class ShellSorter
    { public void Sort(int\[\] list)
        { int inc; for (inc = 1; inc <= list.Length / 9; inc = 3 \* inc + 1) ; for (; inc > 0; inc /= 3)
            { for (int i = inc + 1; i <= list.Length; i += inc)
                { int t = list\[i - 1\]; int j = i; while ((j > inc) && (list\[j - inc - 1\] > t))
                    {
                        list\[j \- 1\] = list\[j - inc - 1\];
                        j \-= inc;
                    }
                    list\[j \- 1\] = t;
                }
            }
        }
    }

复制代码

选择排序

 1.简介

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

2.实现过程

最差时间复杂度 О(n²)

最优时间复杂度 О(n²)

平均时间复杂度 О(n²)

3.C#实现

复制代码

    /// <summary>
    /// 选择排序 /// </summary>
    public class SelectionSorter
    { // public enum comp {COMP\_LESS,COMP\_EQUAL,COMP\_GRTR};
        private int min; // private int m=0;
        public void Sort(int\[\] list)
        { for (int i = 0; i < list.Length - 1; ++i)
            {
                min \= i; for (int j = i + 1; j < list.Length; ++j)
                { if (list\[j\] < list\[min\])
                        min \= j;
                } int t = list\[min\];
                list\[min\] \= list\[i\];
                list\[i\] \= t; // Console.WriteLine("{0}",list\[i\]);

}

        }
    }

复制代码

冒泡排序

1.简介

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序对n个项目需要O(n^{2})的比较次数,且可以原地排序。尽管这个算法是最简单了解和实作的排序算法之一,但它对于少数元素之外的数列排序是很没有效率的。
冒泡排序是与插入排序拥有相等的执行时间,但是两种法在需要的交换次数却很大地不同。在最坏的情况,冒泡排序需要O(n^{2})次交换,而插入排序只要最多O(n)交换。冒泡排序的实现(类似下面)通常会对已经排序好的数列拙劣地执行(O(n^{2})),而插入排序在这个例子只需要O(n)个运算。因此很多现代的算法教科书避免使用冒泡排序,而用插入排序取代之。冒泡排序如果能在内部循环第一次执行时,使用一个旗标来表示有无需要交换的可能,也有可能把最好的复杂度降低到O(n)。在这个情况,在已经排序好的数列就无交换的需要。若在每次走访数列时,把走访顺序和比较大小反过来,也可以稍微地改进效率。有时候称为往返排序,因为算法会从数列的一端到另一端之间穿梭往返。

2.算法实现
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 

3.实现过程

最差时间复杂度 O(n^{2})

最优时间复杂度 O(n)

平均时间复杂度 O(n^{2})

4.C#实现

复制代码

   /// <summary>
    /// 冒泡排序 /// </summary>
    public class bubblesort
    { public void BubbleSort(int\[\] R)
        { int i, j, temp; //交换标志 
            bool exchange; for (i = 0; i < R.Length; i++) //最多做R.Length-1趟排序 

{
exchange = false; //本趟排序开始前,交换标志应为假
for (j = R.Length - 2; j >= i; j–)
{ if (R[j + 1] < R[j]) //交换条件
{
temp = R[j + 1];
R[j + 1] = R[j];
R[j] = temp;
exchange = true; //发生了交换,故将交换标志置为真
}
} if (!exchange) //本趟排序未发生交换,提前终止算法
{ break;
}
}
}
}

复制代码