我能否在C#库中使用全局预处理器定义?

6

在C#中,您可以通过使用类似于C/C++语法的宏来进行条件编译。这将使以下内容成为可能:

#define MYMACRO
....
#if MYMACRO
//some C# code logic 
#else
//some other C# code logic

我需要在C#库项目中的专用文件中定义一些宏,并且当定义后,我需要使这些宏可在整个库内可见。问题是上述代码仅适用于单个文件。
我知道另一种解决方法是将宏添加到构建命令中。这将确保为整个.dll定义宏,我将拥有#if-#else检查,无论我想要在库内何处使用。但是,这种方法存在问题:我想要轻松维护这些宏。它们位于项目内部的文件中将是完美的。我也想在其中添加一些注释,以便我知道每个宏的作用。如果我必须将宏作为构建参数传递,则这将不适用。另一个原因是可以通过对其进行注释并检查行为来简单地打开/关闭宏。
有没有合适的方法来满足我的要求?我希望不必处理任何构建自动化工具,例如MSBuild、NAnt或类似工具,但如果没有其他方法,我会感激一个建议,您认为哪种更好选择。

你可以将定义添加到csproj文件中... - user180326
我需要它们轻松地打开/关闭库的功能,而不影响其暴露的API,但会在不同情况下影响其性能。主要原因之一是为Mono-based环境和标准CLR分别构建不同的版本。使用宏将避免运行时检查,这将影响性能,有些事情可能甚至在运行时都无法知道。此外,针对不同环境预配置版本也更容易。 - Ivaylo Slavov
2
在这种情况下,我认为最好在不同的类中有不同的实现,并根据配置文件(使用IoC容器)注入它们,这可以针对不同的环境进行区分。而且,在应用程序启动时进行一次运行时检查不会影响性能。 - svick
1
@svick,这是一个不错的解决方案,但我担心在我的情况下,这是我想避免的。DI意味着库的使用者应该知道环境依赖关系,并且应该注意适当的注入 - 这使得库不是独立的,并且具有更严格的要求。我追求的主要目的之一是不让使用者代码变得复杂,或者依赖于这些“内部”考虑因素。 - Ivaylo Slavov
2
你可以使用“穷人的依赖注入”(Poor Man's DI),或者在项目中编写一个轻量级容器。这里有一个例子。 这不是我首选的解决方案,但这是一个选择。 - TrueWill
显示剩余5条评论
4个回答

5
通过项目+属性、生成选项卡、"条件编译符号"设置,可以为整个项目定义它们。这将设置项目文件中的元素。您可以通过在msbuild中使用/property:DefineConstants="MYMACRO"命令行选项来覆盖此属性。

谢谢您提供这些信息,但它并不是我需要的确切内容。我需要在标准C#文件中定义宏,并实现与您提到的相同的效果。我开始相信使用VS的标准功能可能无法实现。 - Ivaylo Slavov
2
没错,如果你的首选方法可行,我就不会发表这个答案了。 - Hans Passant
1
这个答案对我有帮助,而不是其他的答案。 - Spongebob Comrade

2
C#中的“预处理器”指令与C预处理器指令不同。对于您来说,最重要的区别是没有等效的#include。在正常情况下,它不是必需的,因为C#没有(也不需要)头文件。我认为除非您创建自己的预处理器或使用#define读取文件并将其转换为msbuild的参数,否则您想要的结果是不可能实现的。
但我认为更容易的方法是使用面向对象的方法:将不同的方法封装到类中并使用它们。要指定使用哪个方法,可以使用依赖注入。这意味着您必须随库一起提供DI库,但我认为这是值得付出的代价。
此外,这种方法还可缓解条件编译的问题:指定不同的符号集可能会以意想不到的方式破坏构建。

2
我建议像@Hans Passant建议的那样,将宏放在项目设置(csproj文件)中。
如果您需要记录定义,请添加一个文档文件到解决方案中,解释这些设置的含义。
如果变量不太多,您可以为每个变量定义一个新的项目配置。这样,您就可以预先配置每个变量所需的#defines列表,然后只需从工具栏中的配置组合框中切换即可。如果您想暂时禁用某个选项,可以复制当前配置并删除#define,然后在测试后删除该配置。
下一个选项是使其更加“容易”(通过将设置和文档组合成单个文件),您可以使用简单的文本文件(包含设置+注释)来配置项目,并花费15分钟编写一个快速的C#应用程序,以读取此文件并将其包含的设置写入.csproj文件 - 它只是XML,因此编写这样的应用程序应该很容易。您可以轻松地调整此文件并运行更新器应用程序以更改项目设置。如果您经常这样做,可以花30分钟添加带有复选框的UI,以更轻松地选择设置。
但是,您描述的概念听起来相当奇怪。库的目的通常是拥有一个标准化的代码块,可以被许多客户端共享,因此经常更改这些定义以重新配置整个库并不是我期望经常需要解决的问题。也许您有充分的理由,但是值得审查为什么需要解决此#define问题。
例如,如果您有许多需要“库”的不同变量的客户端,则最佳方法是使用配置(如上所述)允许您批量构建所有所需的变量。如果只是尝试许多不同的算法/技术,则可以重新设计库的块,使大多数#define的影响仅限于单个.cs文件,因此它们不再需要是全局的。也许库不应该是一个单一的dll,或者需要插件架构来允许您选择包含在库中的“模块”。

感谢您冗长但有用的帖子。我已经开始重新考虑我的预处理器指令方法。我不想在我的库中以任何形式使用 DI,所以我将尝试使用配置来解决问题。您提出的另一个建议是编写一个应用程序,可以将文件添加到 csproj 文件中,并且这也可以作为预构建命令来实现,我想。 - Ivaylo Slavov

1

使用GUI

  1. 在Visual Studio中打开项目
  2. 在解决方案资源管理器中,右键单击项目文件,选择属性
  3. 进入生成选项卡,确保您在配置下拉菜单中选择了所有配置
  4. 平台下拉菜单中确保选择了所有平台
  5. 条件编译符号文本框中输入以分号分隔的所需预处理器定义

enter image description here

添加到项目文件

  1. 在文本编辑器中打开项目文件。
  2. 将以下代码复制并粘贴到现有PropertyGroup的末尾:

    <PropertyGroup Condition="'$(VariableName)'=='VarableValue'"> <DefineConstants>PDEF1;PDEF2;PDEF3</DefineConstants> </PropertyGroup>

  3. 如果不需要添加条件,请删除Condition="'$(VariableName)'=='VarableValue'"部分。

  4. 保存项目文件并从Visual Studio中打开。

来源:https://codeketchup.blogspot.sg/2018/04/how-to-add-project-level-preprocessor.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接