C# 教程 在线

2416C# 高级教程 C# 特性(Attribute)

1、创建一个自定义特性:

// 描述如何使用一个自定义特性 SomethingAttribute
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]    

//********自定义特性SomethingAttribute**************//
public class SomethingAttribute : Attribute    {
    private string name; // 名字
    private string data; // 日期
    public string Name {
        get { return name; }
        set { name = value; }
    }
    public string Data    {
        get { return data; }
        set { data = value; }
    }
    public SomethingAttribute(string name)    {
        this.name = name;
        this.name = name;
    }
}

2、实例化自定义

[Something("Amy", Data = "2018-06-18")]
[Something("Jack", Data = "2018-06-18")]
class Test{}

3、获取自定义特性的中的变量

Type t = typeof(Test);
var something = t.GetCustomAttributes(typeof(SomethingAttribute),true);
foreach(SomethingAttribute each in something)
{
    Console.WriteLine("Name:{0}", each.Name);
    Console.WriteLine("Data:{0}",each.Data);
}

2415C# 高级教程 C# 特性(Attribute)

C# 中利用 Conditional 定义条件方法

利用 Conditional 属性,程序员可以定义条件方法。Conditional 属性通过测试条件编译符号来确定适用的条件。当运行到一个条件方法调用时,是否执行该调用,要根据出现该调用时是否已定义了此符号来确定。如果定义了此符号,则执行该调用;否则省略该调用(包括对调用的参数的计算)。使用Conditional是封闭#if和#endif内部方法的替代方法,它更整洁,更别致、减少了出错的机会。

条件方法要受到以下限制:

  • 条件方法必须是类声明或结构声明中的方法。如果在接口声明中的方法上指定Conditional属性,将出现编译时错误。
  • 条件方法必须具有返回类型。
  • 不能用override修饰符标记条件方法。但是,可以用virtual修饰符标记条件方法。此类方法的重写方法隐含为有条件的方法,而且不能用Conditional属性显式标记。
  • 条件方法不能是接口方法的实现。否则将发生编译时错误。
  • 如果条件方法用在“委托创建表达式”中,也会发生编译时错误

这里需要注意的是:如果创建一个没有定义任何条件的方法,那么默认只要调用就总是会执行此方法,如果你想通过条件来判断执行,那么该方法上必须至少包含一个conditional特性所定义的条件,它才会响应你定义的条件

2414C# 文件的输入与输出

文件路径

文件和文件夹的路径操作都在Path类中。另外还可以用Environment类,里面包含环境和程序的信息。

string dirPath = @"D:\TestDir";
string filePath = @"D:\TestDir\TestFile.txt";
Console.WriteLine("<<<<<<<<<<<{0}>>>>>>>>>>", "文件路径");
//获得当前路径
Console.WriteLine(Environment.CurrentDirectory);
//文件或文件夹所在目录
Console.WriteLine(Path.GetDirectoryName(filePath));     //D:\TestDir
Console.WriteLine(Path.GetDirectoryName(dirPath));      //D:\
//文件扩展名
Console.WriteLine(Path.GetExtension(filePath));         //.txt
//文件名
Console.WriteLine(Path.GetFileName(filePath));          //TestFile.txt
Console.WriteLine(Path.GetFileName(dirPath));           //TestDir
Console.WriteLine(Path.GetFileNameWithoutExtension(filePath)); //TestFile
//绝对路径
Console.WriteLine(Path.GetFullPath(filePath));          //D:\TestDir\TestFile.txt
Console.WriteLine(Path.GetFullPath(dirPath));           //D:\TestDir  
//更改扩展名
Console.WriteLine(Path.ChangeExtension(filePath, ".jpg"));//D:\TestDir\TestFile.jpg
//根目录
Console.WriteLine(Path.GetPathRoot(dirPath));           //D:\      
//生成路径
Console.WriteLine(Path.Combine(new string[] { @"D:\", "BaseDir", "SubDir", "TestFile.txt" })); //D:\BaseDir\SubDir\TestFile.txt
//生成随即文件夹名或文件名
Console.WriteLine(Path.GetRandomFileName());
//创建磁盘上唯一命名的零字节的临时文件并返回该文件的完整路径
Console.WriteLine(Path.GetTempFileName());
//返回当前系统的临时文件夹的路径
Console.WriteLine(Path.GetTempPath());
//文件名中无效字符
Console.WriteLine(Path.GetInvalidFileNameChars());
//路径中无效字符
Console.WriteLine(Path.GetInvalidPathChars()); 

2413C# 文件的输入与输出

文件属性操作

File类与FileInfo都能实现。静态方法与实例化方法的区别!

//use File class
Console.WriteLine(File.GetAttributes(filePath));
File.SetAttributes(filePath,FileAttributes.Hidden | FileAttributes.ReadOnly);
Console.WriteLine(File.GetAttributes(filePath));

//user FilInfo class
FileInfo fi = new FileInfo(filePath);
Console.WriteLine(fi.Attributes.ToString());
fi.Attributes = FileAttributes.Hidden | FileAttributes.ReadOnly; //隐藏与只读
Console.WriteLine(fi.Attributes.ToString());

//只读与系统属性,删除时会提示拒绝访问
fi.Attributes = FileAttributes.Archive;
Console.WriteLine(fi.Attributes.ToString());

2412C# 预处理器指令

预处理器指令的用途理解:

在程序调试和运行上有重要的作用。比如预处理器指令可以禁止编译器编译代码的某一部分,如果计划发布两个版本的代码,即基本版本和有更多功能的企业版本,就可以使用这些预处理器指令来控制。在编译软件的基本版本时,使用预处理器指令还可以禁止编译器编译于额外功能相关的代码。另外,在编写提供调试信息的代码时,也可以使用预处理器指令进行控制。总的来说和普通的控制语句(if等)功能类似,方便在于预处理器指令包含的未执行部分是不需要编译的。

#define PI
using System;
namespace PreprocessorDAppl
{
   class Program
   {
      static void Main(string[] args)
      {
         #if (PI)
            Console.WriteLine("PI is defined");     //PI不存在,则这条语句不编译
         #else
            Console.WriteLine("PI is not defined"); //PI存在,则这条语句不编译
         #endif
         Console.ReadKey();
      }
   }
}

其他预处理器指令:

#warning 和 #error:

当编译器遇到它们时,会分别产生警告或错误。如果编译器遇到 #warning 指令,会给用户显示 #warning 指令后面的文本,之后编译继续进行。如果编译器遇到 #error 指令,就会给用户显示后面的文本,作为一条编译错误消息,然后会立即退出编译。使用这两条指令可以检查 #define 语句是不是做错了什么事,使用 #warning 语句可以提醒自己执行某个操作。

#if DEBUG && RELEASE  
#error "You've defined DEBUG and RELEASE simultaneously!"  
#endif  
#warning "Don't forget to remove this line before the boss tests the code!"  
Console.WriteLine("*I hate this job.*");

2. #region 和 #endregion

#region 和 #endregion 指令用于把一段代码标记为有给定名称的一个块,如下所示:

#region Member Field Declarations
int x;
double d;
Currency balance;
#endregion

这看起来似乎没有什么用,它不影响编译过程。这些指令的优点是它们可以被某些编辑器识别,包括 Visual Studio .NET 编辑器。这些编辑器可以使用这些指令使代码在屏幕上更好地布局。

3. #line

#line 指令可以用于改变编译器在警告和错误信息中显示的文件名和行号信息,不常用。

如果编写代码时,在把代码发送给编译器前,要使用某些软件包改变输入的代码,就可以使用这个指令,因为这意味着编译器报告的行号或文件名与文件中的行号或编辑的文件名不匹配。#line指令可以用于还原这种匹配。也可以使用语法#line default把行号还原为默认的行号:

#line 164 "Core.cs" // 在文件的第 164 行
// Core.cs, before the intermediate
// package mangles it.
// later on
#line default // 恢复默认行号

4. #pragma

#pragma 指令可以抑制或还原指定的编译警告。与命令行选项不同,#pragma 指令可以在类或方法级别执行,对抑制警告的内容和抑制的时间进行更精细的控制。如下:

#pragma warning disable 169    // 取消编号 169 的警告(字段未使用的警告)
public class MyClass
{
    int neverUsedField;       // 编译整个 MyClass 类时不会发出警告
}
#pragma warning restore 169   // 恢复编号 169 的警告