C#条件编译、内联函数、CLS介绍

1、条件编译

#if 条件编译会隐藏非条件(#else if)代码,我们开发中很可能会忽略掉这部分代码,当我们切换条件常量到这部分代码时,很可能因为各种原因导致报错。

如果使用特性进行条件编译标记,在开发过程中就可以留意到这部分代码。

[Conditional("DEBUG")]

例如,当使用修改所有引用-修改一个类成员变量或者静态变量名称时,#if 非条件中的代码不会被修改,因为这部分代码“无效”,而且使用 [Conditional("DEBUG")] 的代码则跟条件无关,会被同步修改。

Conditional 特性标记的方法等,在开发过程中保持有效,当在编译时可能被排除。

代码片段只能使用 #if 了,如果是单个方法,则可以使用 Conditional 。

2、MethodImpl 特性

此特性在 System.Runtime.CompilerServices 命名空间中,指定如何实现方法的详细信息。

内联函数使用方法可参考 https://www.jb51.net/article/242567.htm

MethodImpl 特性可以影响 JIT 编译器的行为。

无法使用 MemberInfo.GetCustomAttributes 来获取此特性的信息,即不能通过获取特性的方法获取跟 MethodImpl 有关的信息(反射),只能调用 MethodInfo.GetMethodImplementationFlags() 或 ConstructorInfo.GetMethodImplementationFlags () 来检索。

MethodImpl 可以在方法以及构造函数上使用。

MethodImplOptions 用于设置编译行为,枚举值可组合使用,其枚举说明如下:

枚举 枚举值 说明
AggressiveInlining 256 如可能应将该方法进行内联。
AggressiveOptimization 512 此方法包含一个热路径,且应进行优化。
ForwardRef 16 已声明该方法,但在其他位置提供实现。
InternalCall 4096 该调用为内部调用,也就是说它调用了在公共语言运行时中实现的方法。
NoInlining 8 该方法不能为内联方法。 内联是一种优化方式,通过该方式将方法调用替换为方法体。
NoOptimization 64 调试可能的代码生成问题时,该方法不由实时 (JIT) 编译器或本机代码生成优化(请参阅 Ngen.exe)。
PreserveSig 128 完全按照声明导出方法签名。
Synchronized 32 该方法一次性只能在一个线程上执行。 静态方法在类型上锁定,而实例方法在实例上锁定。 只有一个线程可在任意实例函数中执行,且只有一个线程可在任意类的静态函数中执行。
Unmanaged 4 此方法在非托管的代码中实现。

Synchronized 修饰的方法可以避免多线程中的一些问题,但是不建议对公共类型使用锁定实例或类型上的锁定,因为 Synchronized 可以对非自己的代码的公共类型和实例进行锁定。 这可能会导致死锁或其他同步问题。

意思是说,如果共享的成员已经设置了锁,那么不应该再在 Synchronized 方法中使用,这样双重锁定容易导致死锁以及其他问题。

3、CLSCompliantAttribute

指示程序元素是否符合公共语言规范 (CLS)。

CLS规范可参考:

https://docs.microsoft.com/en-us/dotnet/standard/language-independence

全局开启方法:

程序目录下添加一个 AssemblyAttribytes.cs 文件,或者打开 obj 目录,找到 AssemblyAttributes.cs 结尾的文件,如 .NETCoreApp,Version=v3.1.AssemblyAttributes.cs,添加:

using System;	// 这行已经有的话不要加
[assembly: CLSCompliant(true)]

之后就可以在代码中使用 [CLSCompliant(true)] 特性。

局部开启:

也可以放在类等成员上使用:

[assembly: CLSCompliant(true)]

您可以将特性应用于 CLSCompliantAttribute 下列程序元素:程序集、模块、类、结构、枚举、构造函数、方法、属性、字段、事件、接口、委托、参数和返回值。 但是,CLS 遵从性的概念仅适用于程序集、模块、类型和类型的成员。

程序编译时默认不会检查代码是否符合 CLS 要求,但是如果你的可以是公开的(代码共享、Nuget 发布等),则建议使用使用 [assembly: CLSCompliant(true)] ,指明你的库符合 CLS 要求。

在团队开发中以及内部共享代码时,高质量的代码尤为重要,所以有必要使用工具检查代码,如 roslyn 静态分析、sonar 扫描等,也可以使用上面的特性,自动使用 CLS 检查。

CLS 部分要求:

  • 无符号类型不应成为该类的公共接口的一部分(私有成员可以使用),例如 UInt32 这些属于 C# 的类型,但不是 CLS “标准” 中的。
  • 指针等不安全类型不能与公共成员一起使用,就是公有方法中都不应该使用 unsafe 代码。(私有成员可以使用)。
  • 类名和成员名不应重名。虽然 C# 中区分大小写,但是 CLS 不建议同名非重载函数,例如 MYTEST 跟 Mytest。
  • 只能重载属性和方法,不应重载运算符。重载运算符容易导致调用者不知情时出现程序错误,并且重载运算符要排查问题十分困难。

我们可以编译以下代码,尝试使用 CLSCompliant :

[assembly: CLSCompliant(true)]
[CLSCompliant(true)]
public class Test
{
    public void MyMethod()
    {
    }
    public void MYMETHOD()
    {
    }
}

IDE 中会警告:warning CS3005: 仅大小写不同的标识符“Test.MYMETHOD()”不符合 CLS,编译时也会提示 Warn。当然,不会阻止编译,也不会影响程序运行。

总之,如果要标记一个程序集 CLS 规范,可以使用 [assembly: CLSCompliant(true)] 特性。

[CLSCompliant(true)] 特性指示这个元素符合 CLS 规范,这时编译器或者 IDE 会检查你的代码,检查是否真的符合规范。

如果偏偏要写不符合规范的代码,则可以使用 [CLSCompliant(false)]

4、必要时自定义类型别名

C# 也可以定义类型别名。

using intbyte = System.Int32;
using intkb = System.Int32;
using intmb = System.Int32;
using intgb = System.Int32;
using inttb = System.Int32;
        byte[] fileByte = File.ReadAllBytes("./666.txt");
        intmb size = fileByte.Length / 1024;

一些情况下,使用别名可以提高代码可读性。真实项目不要使用以上代码,我只是写个示例,这并不是合适的应用场景。

到此这篇关于C#条件编译、内联函数、CLS的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#获取摄像头拍照显示图像的方法

    本文实例为大家分享了C#获取摄像头拍照显示图像的具体代码,供大家参考,具体内容如下 概述 之前有个需求,就是在web界面可以实现调用摄像头,用户把手机的个人二维码展示给摄像头,摄像头进行摄像识别用户. 其实本质就是保存图像二维码,在进行二维码识别. 下面来看看如何实现. 主要代码实现 1.初始化摄像头 /// <summary>         /// 初始化摄像头         /// </summary>         /// <param name="ha

  • C#枚举的高级应用

    文章开头先给大家出一道面试题: 在设计某小型项目的数据库(假设用的是 MySQL)时,如果给用户表(User)添加一个字段(Roles)用来存储用户的角色,你会给这个字段设置什么类型?提示:要考虑到角色在后端开发时需要用枚举表示,且一个用户可能会拥有多个角色. 映入你脑海的第一个答案可能是:varchar 类型,用分隔符的方式来存储多个角色,比如用 1|2|3 或 1,2,3 来表示用户拥有多个角色.当然如果角色数量可能超过个位数,考虑到数据库的查询方便(比如用 INSTR 或 POSITION

  • C#调用USB摄像头的方法

    C#调用USB摄像头使用AForge类库进行开发,供大家参考,具体内容如下 1.AForge安装 右击工程,在管理NuGet程序包中搜索Aforge类库,选择安装,如下图所示 2.进行USB摄像头类封装 a.初始化,初始化时要注意,加载的设备分辨率需要人工配置,如果配置分辨率不存在需要从默认的分辨率中选择 videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);   if (videoDevices.Cou

  • C#中内联函数的用法介绍

    C++ 中有个内联函数,使用 inline 来修饰函数,编译器就会对其进行优化,将此函数作为代码判断插入到调用处. 函数调用在执行时,首先要在栈中为形参和局部变量分配存储空间,然后还要将实参的值复制给形参,接下来还要将函数的返回地址(该地址指明了函数执行结束后,程序应该回到哪里继续执行)放入栈中,最后才跳转到函数内部执行.这个过程是要耗费时间的. 另外,函数执行 return 语句返回时,需要从栈中回收形参和局部变量占用的存储空间,然后从栈中取出返回地址,再跳转到该地址继续执行,这个过程也要耗费

  • C#文件操作、读取文件、Debug/Trace类用法

    1.文件操作 这段代码在 System.Private.CoreLib 下,对 System.IO.File 中的代码进行精简,供 CLR 使用. 当使用文件时,要提前判断文件路径是否存在,日常项目中要使用到文件的地方应该不少,可以统一一个判断文件是否存在的方法: public static bool Exists(string? path) { try { // 可以将 string? 改成 string if (path == null) return false; if (path.Len

  • 如何使用正则表达式判断邮箱(以C#为例)

    目录 首先引入正则需要使用的命名空间 判断是否为QQ邮箱 判断是否为邮箱 补充:C#正则表达式(Regex类) 总结 日常开发中,判断邮箱是少不了的,这个我以**C#**为例,来写一个判断方法,正则表达式是通用的,CV就可以 首先引入正则需要使用的命名空间 //正则验证引用 using System.Text.RegularExpressions; 判断是否为QQ邮箱 /// <summary> /// 验证QQ邮箱 /// </summary> /// <param nam

  • C#实现文本转语音功能

    由于最近的工作需要用到文本转语音的功能,在网上找到的资料有些不完整,特此记录下整个完整功能. 这种方式的优点在于不会被浏览器限制,在js的文本转语音功能中,谷歌高版本的浏览器会阻止通过模拟点击的自动播放,而ie不会阻止. 一.确认研发环境 操作系统:win10或win7(我自己用的是win10 据说有些阉割版的win7会报错) IDE:VS2012 (可高于此版本) .NET framework 4.0(可高于此版本) 二.系统自带语音识别功能 1.C:\Windows文件夹下有Speech 2

  • C#使用DirectX.DirectSound播放语音

    最近在做项目时,需要进行音频文件的即时播放,并且要求同时播放多条语音,之前C#程序中语音播放一直使用System.Media类库的SoundPlayer类进行播放,但是这个播放类有个弊端,就是在播放时不能抢占式播放语音,经过查找资料DirectX.DirectSound可同时播放多条语音. DirectX.DirectSound的特点 1.可同时播放多条语音 2.可分左右声道进行播放 3.可随时释放正在播放的语音 此组件处理流程: 1.创建播放线程 public void StartDirect

  • C#实现WPF项目复制和移动文件夹

    使用WPF做的一个简单的操作文件的demo,包括复制和移动文件夹,核心思想就是使用递归,如果只是移动或者复制单一文件,直接使用File.Copy()或者File.Move()方法即可. XAML代码 <Window x:Class="OperationFile.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://sc

  • C#条件编译、内联函数、CLS介绍

    1.条件编译 #if 条件编译会隐藏非条件(#else if)代码,我们开发中很可能会忽略掉这部分代码,当我们切换条件常量到这部分代码时,很可能因为各种原因导致报错. 如果使用特性进行条件编译标记,在开发过程中就可以留意到这部分代码. [Conditional("DEBUG")] 例如,当使用修改所有引用-修改一个类成员变量或者静态变量名称时,#if 非条件中的代码不会被修改,因为这部分代码“无效”,而且使用 [Conditional("DEBUG")] 的代码则跟

  • c++内联函数(inline)使用详解

    介绍内联函数之前,有必要介绍一下预处理宏.内联函数的功能和预处理宏的功能相似.相信大家都用过预处理宏,我们会经常定义一些宏,如 复制代码 代码如下: #define TABLE_COMP(x) ((x)>0?(x):0) 就定义了一个宏. 为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方.这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行.因此,函数调

  • 深入探讨:宏、内联函数与普通函数的区别

    内联函数的执行过程与带参数宏定义很相似,但参数的处理不同.带参数的宏定义并不对参数进行运算,而是直接替换:内联函数首先是函数,这就意味着函数的很多性质都适用于内联函数,即内联函数先把参数表达式进行运算求值,然后把表达式的值传递给形式参数.    内联函数与带参数宏定义的另一个区别是,内联函数的参数类型和返回值类型在声明中都有明确的指定:而带参数宏定义的参数没有类型的概念,只有在宏展开以后,才由编译器检查语法,这就存在很多的安全隐患.    使用内联函数时,应注意以下问题:    1)内联函数的定

  • C语言中的内联函数(inline)与宏定义(#define)详细解析

    先简明扼要,说下关键:1.内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快. 2.内联函数可以调试,而宏定义是不可以调试的.内联函数与宏本质上是两个不同的概念如果程序编写者对于既要求快速,又要求可读的情况下,则应该将函数冠以inline.下面详细介绍一下探讨一下内联函数与宏定义. 一.内联函数是什么?内联函数是代码被插入到调用者代码处的函数.如同 #define 宏(但并不等同,原因见下文),内联函数通过避免被调用的开销来提

  • c++中的内联函数inline用法实例

    问题描述:类中成员函数缺省默认是内联的,如果在类定义时就在类内给出函数定义,那当然最好.如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的.内联函数的inline要加在函数前面,不可以加在声明前面. class A { public:void Foo(int x, int y) { } // 自动地成为内联函数 } //正确写法: // 头文件 class A { public: void Foo(int x, int y); } // 定义文

  • C++ inline内联函数详解

    函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码.CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码:被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行. 一个 C/C++ 程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是 main(),终点也是 main().当 main() 调用完了所有的函数,它会返回一个值(例如return 0;)来结束自己的生

  • c++ 内联函数和普通函数的区别

    前言 内联函数是c++为了提高程序的运行速度做的改进,它与普通函数区别在于: 编译器如何将它们组合到程序中.所以我们需要深入到程序内部. 我们的最终的可执行程序由 一组机器指令组成.程序运行时,计算机逐步执行指令. Ⅰ.常规函数 常规函数调用时会使程序跳到另一个地址(函数的地址),并且在函数结束时返回. 执行函数调用指令,立即存储该指令的地址,并将函数参数保存到的堆栈. 跳到函数起点的内存单元,执行函数代码(将返回值保存到寄存器中. 跳回被保存指令的地址处. 这一过程和系统中的中断很类似.来回跳

  • C++入门(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围for)

    一.C++关键字 C++总共有63个关键字,在入门阶段我们只是大致了解一下就可,在后续博客中会逐渐讲解 二.命名空间 相信学过C++的同学,一定都写过下面这个简单的程序 #include<iostream> using namespace std; int main() { cout<<"hello world"<<endl; return 0; } 我们先来看第二行代码,using namespace std , 这行代码是什么意思呢 ? 这里我们

  • C++ 内联函数inline案例详解

    使用函数能够避免将相同代码重写多次的麻烦,还能减少可执行程序的体积,但也会带来程序运行时间上的开销. 函数调用在执行时,首先要在栈中为形参和局部变量分配存储空间,然后还要将实参的值复制给形参,接下来还要将函数的返回地址(该地址指明了函数执行结束后,程序应该回到哪里继续执行)放入栈中,最后才跳转到函数内部执行.这个过程是要耗费时间的. 另外,函数执行 return 语句返回时,需要从栈中回收形参和局部变量占用的存储空间,然后从栈中取出返回地址,再跳转到该地址继续执行,这个过程也要耗费时间. 总之,

随机推荐