C# .NET 开发心得

1. 工作路径问题

1. 多项目构成的解决方案,Web APP作为启动项目时的工作路径

//当前执行的exe文件名
//C:\\Program Files\\IIS Express\\iisexpress.exe
System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName
//获取和设置当前目录(该进程从中启动的目录)的完全限定目录
//C:\\Program Files\\IIS Express
System.Environment.CurrentDirectory
//获取应用程序当前工作目录。这个不一定是程序从中启动的目录。这是任何应用程序最后一次操作过的目录,比如你用Word打开了E:\doc\my.doc这个文件,此时执行这个方法就返回E:\doc了
//"C:\\Program Files\\IIS Express
System.IO.Directory.GetCurrentDirectory()
//获取程序的基目录,往往是项目实际存储的目录
//D:\\Code\\location\\XiaKe\\XiaKeWebAPI\\
System.AppDomain.CurrentDomain.BaseDirectory
//获取和设置包括该应用程序的目录的名词
//D:\\Code\\location\\XiaKe\\XiaKeWebAPI\\
System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase

 2. 程序集影像复制问题

1. bin, obj, properties 目录

bin 目录用来存放编译的结果,对应的文件夹下有 Debug 和 Release 两个版本,为默认的二进制文件输出路径。

obj 目录用来存放编译过程中生成的中间临时文件,其中也对应有调试版本和发布版本,在 .NET 中,编译是分模块进行的,编译整个完成后会合并为一个 .DLL 或 .EXE 保存到 bin 目录下。因为每次编译时都默认采用增量编译,即只重新编译改变了的模块,obj 保存每个模块的编译结果,用来加快编译速度。是否采用增量编译,可以通过:项目属性->配置属性->高级->增量编译来设置。

properties 文件夹定义程序集的属性,一般只有 AssemblyInfo.cs 类文件,用于保存程序集的信息,如名称,版本等,这些信息一般与项目属性面板中的数据对应,不需要手动编写。

2. shadowCopyBinAssembliies 属性

在 ASP .NET <system.web><hostingEnvironment shadowCopyBinAssembliies="false" /></system.web> 里设置,默认为 true,表示 Bin 目录中的应用程序的程序集是否影像复制到该应用程序的 ASP.NET 临时文件目录中。在有时候对程序进行修改后,可能会出现运行结果仍然保持未修改前的状态,是因为直接提取临时文件目录下的程序集,这种情况下应该手动设置为 false。

3. Debug 提示 "The application is in a break mode." 问题

1. 弹出错误提示,然后直接终止程序,通过设置 Debug | Windows | Exception Settings 就可以配置调试器中断处的异常类型,可以跟踪抛出异常的位置和信息。

4. Unhandled Exception: System.BadImageFormatException: Could not load file or assembly 'Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.

无法加载依赖项,比如 DLL,可能是因为 DLL 的位数和当前程序的位数不一样,需要通过 configuration manger 设置为合适的程序位数 Any CPU | x86 | x64。

5. 正则表达式的贪婪匹配与非贪婪匹配

  <div>.*</div> 为贪婪匹配,会尽可能匹配到最远处;<div>.*?</div> 为非贪婪匹配,匹配到合适的表达式,就结束当前匹配,尽可能少重复。

6. Git 

1. 在clone一个代码仓库时,得到的都是其主分支origin/master(origin表示远端服务器)到本地master,然后可以check out不同的分支到本地,在不同的分支间进行切换时,本地文件系统会随着发生变化,时刻展现当前分支的文件内容。

7. List<T>排序

Entities.OrderBy(x => x.Start).ToList()

8. 判断一个IEnumerable<T>的对象中是否包含元素

Entities.Any() 这个函数在命令空间 System.Linq 下

9. Inconsistent Accessibility: Parameter type is less accessible than method

可能原因是构造函数是public,但是所需要的参数是其他类的private。

10. Unit Test 对异常进行测试的两种方式

大多数情况下使用ExpectedException属性就可以满足需求。但是如果需要检查Exception被捕获时,条件或者参数值是否成立。可以使用更加复杂的方式。

//Solution 1
//下面大括号内可以填入异常信息,但是不会自动校验
[TestMethod]
[ExpectedException(typeof(ArgumentNullException){, "ExceptionMessage})]
public void MethodTest()
{
    var obj = new ClassRequiringNonNullParameter( null );
}

//Solution 2
public void MethodTest()
{
    try
    {
        var obj = new ClassRequiringNonNullParameter( null );
        Assert.Fail("An exception should have been thrown");
    }
    catch (ArgumentNullException ae)
    {
        Assert.AreEqual("Parameter cannot be null or empty.", ae.Message);
    }
    catch (Exception e)
    {
        Assert.Fail(
            string.Format("Unexpected exception of type {0} caught: {1}",
                                e.GetType(), e.Message));
    }
}

11. 如何将依赖文件夹拷贝到输出Bin目录下

*.csproj 中 <ItemGroup></ItemGroup> 中设置 <CopyToOutputDirectory> 属性

//<CopyToOutputDirectory>包含三种值
//1. Never
//2. Always
//3. PreserveNewest
<ItemGroup>
    <None Include="App.config" />
    <None Include="NerModel\model.en.all.v1.dat">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="NerModel\model.zh.v1.dat">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

12. ”mscorlib.pdb not loaded" yet the mscorlib.dll is NOT missing

Goto Tools, Options, Debugging, General, Enable Just My Code

This will prevent the debugger from trying to launch on a Internal .NET framework Assembly.

13. readonly 和 const 的区别

1. readonly 为运行时常量,程序运行时给其赋值,初始化都便无法更改。readonly 只能用来修饰类的字段(包括实例类型和静态类型),可以在声明的同时初始化或者在构造函数里初始化。

2. const 为编译时常量,在编译过程中直接被替换成字面量,使得其取值范围只能是数字(整数、浮点数)、字符串以及枚举类型。不仅可以声明为类字段,还可以声明为方法中的局部常量,默认为静态类型(无需用static修饰,否则将导致编译错误),但必须在声明的同时完成初始化。

3. 在可维护性方面,readonly 以引用的形式工作,某个常量更新后所有用到这个常量的地方都能得到更新值。但是 const 在跨程序集里使用时,除了要将包含 const 变量的程序集重新编译,还需要将引用到这个变量的其他程序集重新编译才能生效。

4. 在性能方面,由于 const 是直接将字面值进行编译参与运算,性能略高于 readonly,但是对一般的应用来说,这样的性能提升可以忽略不计。

5. 一般情况下,只有在常量值恒久不变(圆周率、一天包含的小时数)和对程序性能要求严格的情况下使用 const,其他情况下使用 readonly 更合适。比较C#中的readonly与const

14. 枚举类型

枚举类型提供了一种有效的方式来定义可能分配给变量的一组已命名整数常量。通过Enum.ToString("G")可以返回其对应的字符串值,即其命名。

enum Day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
enum Month : byte { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

15. 项目依赖和编译顺序

在一个解决方案中项目的编译顺序是由依赖关系自动生成的,通过改变依赖关系来影响编译顺序。Solution -> Right Click -> Project Build Order。

Rebuild 一个解决方案的过程中,所有项目中的 CopyToOutputDirectory 指定的文件都会复制到所有项目的输出目录中。但是需要注意的是,再之后如果单独编译某个项目(build 增量编译),那么其没有依赖的项目中复制过来的文件会被认为是多余文件被抹去,添加项目依赖后才会保持。(因为在编译输出的过程中,如果输出目录中已经包含同名文件,那么后进行拷贝的文件就不会继续进行)

点击项目 build:只有在对项目文件进行更改时,点击 build 会单独进行该项目的编译,否则不会作任何动作。如果依赖项发生变化也会导致依赖项和当前项目一起重新编译,避免由于输入变化产生的冲突。项目编译其实生成的是一个 dll 动态链接库文件,删除之后会导致该项目重新编译。输出文件夹其他拷贝过来的文件删除后不会重新编译。

点击项目 rebuild:Rebuild 当前项目会导致其依赖项整体重新编译。

16. 项目之间项目引用无效

可能项目基于的框架版本不同导致的,.NET framework 版本必须一致。

17. 测试单元无法调用动态链接库(在输出目录中存在)

测试单元使用处理器的设置与 .dll 文件的版本不同。Test -> Test Setting -> Default Proccessor Architecture -> X64。

18. Azure 项目及追踪日志

1. Azure 项目

如果要将当前的 WebAPI 项目布到 Azure 上,需要在解决方案里新建一个 Azure Web 项目,然后将对应的 WebAPI 加入成对应的 Web Role 即可从 Azure 项目启动。本地启动时需要下载 Azure 环境的 Emulator,Storage Emulator 打开应用程序会自动运行,Compute Emulator 在启动 Azure 项目时会运行。本地调试 Azure 项目时可能会遇到无法启动的情况,一种解决方式是对IIS进行一些设置(Control Panel -> Programs and Features -> Turn Windows features on or off -> 打开一些 IIS 功能 或者 win + R -> inetmgr 打开 IIS Manager 将 Application Pools 的 Identity 改成 LocalSystem),另一种解决办法是直接用 Administrator 权限运行 VS。

2. 追踪日志

使用 System.Diagnostics.Trace 产生诊断信息,使用 Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener 追踪信息,会自动将信息存储在 Storage Account 的 WADLogsTable 当中。(本地调试可能转入 Table 方面不太准确,可以通过程序的 Output 进行查看)另外,需要在 Azure 的 Web Role 设置 Diagnostics 的相关配置,包括存储周期以及日志等级。在 Web.config 加入 DiagnosticMonitorTraceListener 如下:

<system.diagnostics>
    <trace>
      <listeners>
        <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
          name="AzureDiagnostics">
          <filter type="" />
        </add>
      </listeners>
    </trace>
  </system.diagnostics>

19. HttpRequestMessage 内容体

By design the body content in ASP.NET Web API is treated as forward-only stream that can be read only once. The first read is being done when Web API is binding the Request, after that the Request.Content will not return anything. What if we want to load the Content?

第一种方式:将 Web API 方法里的对象参数去掉,只留下 Url 参数,然后手动的从 Request 中加载 Content。

第二种方式:添加一个继承于 DelegatingHandler 的 MessageHandler,在请求送达 Web API 之前把内容读出来,然后 Web API 绑定时会自动重置 stream 读出对应的内容。

其他方式:采用流操作的方式,比如 await request.Content.LoadIntoBufferAsync()。参考

20. Async/Await - Best Practices in Asynchronous Programming

Async/Await - Best Practices in Asynchronous Programming

  知乎:当我们讨论线程安全时,在讨论什么?

21. Stream Object

流操作是先写到 IO 缓存区,然后 flush 到硬盘上,因此如果没有 flush 完程序就结束了,会导致部分输出缺失。可以用 using 的方式给定作用域,能自动的 flush 和关闭流,并且 dispose。

// Example #4: Append new text to an existing file.
        // The using statement automatically flushes AND CLOSES the stream and calls 
        // IDisposable.Dispose on the stream object.
        using (System.IO.StreamWriter file = 
            new System.IO.StreamWriter(@"C:\Users\Public\TestFolder\WriteLines2.txt", true))
        {
            file.WriteLine("Fourth line");
        }

22. Console Code Page

在英文系统中,Console 的默认代码页是 437(OEM -United States),不支持中文输入输出,这与 Visual Studio 无关,与 Console 系统设置有关。通过 Win + R 打开注册表编辑器(regedit),修改HKEY_CURRENT_USER\Console 下对应的控制台(包含 Visual Studio 控制台),增加 DWORD Value,CodePage 为 936(Simplified GBK) 即可。也可以增加 String,AutoRun 为 chcp 936。

23. 访问权限修饰符

public:对任何类和成员都公开,无限制访问。

protected:仅仅对该类和其派生类公开。

private:仅仅对该类公开。

internal:只能在包含该类的程序集中访问该类(只是单独的项目,而不是整个解决方案)。

protected internal:只能在本类,派生类或者包含该类的程序集中访问。

代码风格和Tips

1. 大括号的回括和下面代码之间加一个空行。

2. 代码中不要出现单独的数字或者字符串,很难维护而且含义不明,用常量替代。

  3. 尽可能复用现有的数据结构。

  4. 避免在一个类中声明另一个类的实例,降低不同类的耦合性,通过接口和构造函数传递参数。

  5. 同一个类不同方法之间空一行,数据成员和属性之间不用空行。

 

posted @ 2017-12-29 11:30  hopelee  阅读(1734)  评论(0编辑  收藏  举报