在 C# 代码中重用 .h 文件中的定义语句

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/100854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 14:02:05  来源:igfitidea点击:

Reuse define statement from .h file in C# code

提问by tomash

I have C++ project (VS2005) which includes header file with version number in #define directive. Now I need to include exactly the same number in twin C# project. What is the best way to do it?

我有 C++ 项目(VS2005),它在 #define 指令中包含带有版本号的头文件。现在我需要在双 C# 项目中包含完全相同的数字。最好的方法是什么?

I'm thinking about including this file as a resource, then parse it at a runtime with regex to recover version number, but maybe there's a better way, what do you think?

我正在考虑将此文件作为资源包含在内,然后在运行时使用正则表达式解析它以恢复版本号,但也许有更好的方法,您怎么看?

I cannot move version outside .h file, also build system depends on it and the C# project is one which should be adapted.

我不能将版本移到 .h 文件之外,构建系统也依赖于它,而 C# 项目是一个应该适应的项目。

采纳答案by Jorge Ferreira

You can achieve what you want in just a few steps:

只需几个步骤,您就可以实现您想要的:

  1. Create a MSBuild Task - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. Update the project file to include a call to the task created prior to build
  1. 创建 MSBuild 任务 - http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. 更新项目文件以包含对构建之前创建的任务的调用

The task receives a parameter with the location of the header .h file you referred. It then extracts the version and put that version in a C# placeholder file you previously have created. Or you can think using AssemblyInfo.cs that normally holds versions if that is ok for you.

该任务接收一个参数,其中包含您引用的标头 .h 文件的位置。然后提取版本并将该版本放入您之前创建的 C# 占位符文件中。或者您可以考虑使用通常包含版本的 AssemblyInfo.cs,如果这对您来说没问题。

If you need extra information please feel free to comment.

如果您需要更多信息,请随时发表评论。

回答by aku

MSDN tells us:

MSDN 告诉我们:

The #define directive cannot be used to declare constant values as is typically done in C and C++. Constants in C# are best defined as static members of a class or struct. If you have several such constants, consider creating a separate "Constants" class to hold them.

#define 指令不能像通常在 C 和 C++ 中那样用于声明常量值。C# 中的常量最好定义为类或结构的静态成员。如果您有多个这样的常量,请考虑创建一个单独的“常量”类来保存它们。

You can create library using managed C++ that includes class - wrapper around your constants. Then you can reference this class from C# project. Just don't forget to use readonly < type >instead of const < type >for your constants declaration :)

您可以使用托管 C++ 创建库,其中包含类 - 围绕您的常量的包装器。然后你可以从 C# 项目中引用这个类。只是不要忘记在常量声明中使用readonly < type >而不是const < type >:)

回答by Skizz

You could always use the pre-build event to run the C preprocessor on the .cs file and the post build event to undo the pre-build step. The preprocessor is just a text-substitution system, so this is possible:

您始终可以使用预构建事件在 .cs 文件上运行 C 预处理器,并使用后构建事件来撤消预构建步骤。预处理器只是一个文本替换系统,所以这是可能的:

// version header file
#define Version "1.01"

// C# code
#include "version.h"
// somewhere in a class
string version = Version;

and the preprocessor will generate:

预处理器将生成:

// C# code
// somewhere in a class
string version = "1.01";

回答by Ilya

You can write simple C++/C utility that include this .h file and dynamically create file that can be used in C#.
This utility can be run as a part of C# project as a pre-build stage.
This way you are always sync with the original file.

您可以编写包含此 .h 文件的简单 C++/C 实用程序,并动态创建可在 C# 中使用的文件。
此实用程序可以作为 C# 项目的一部分作为预构建阶段运行。
这样您就始终与原始文件同步。

回答by Corey Trager

I wrote a python script that converts #define FOO "bar" into something usable in C# and I'm using it in a pre-build step in my C# project. It works.

我编写了一个 python 脚本,将 #define FOO "bar" 转换为可在 C# 中使用的内容,我在 C# 项目的预构建步骤中使用它。有用。

# translate the #defines in messages.h file into consts in MessagesDotH.cs

import re
import os
import stat

def convert_h_to_cs(fin, fout):
    for line in fin:
        m = re.match(r"^#define (.*) \"(.*)\"", line)
        if m != None:
            if m.group() != None:
                fout.write( "public const string " \
                + m.group(1) \
                + " = \"" \
                + m.group(2) \
                + "\";\n" )
        if re.match(r"^//", line) != None:
            fout.write(line)

fin = open ('..\common_cpp\messages.h')
fout = open ('..\user_setup\MessagesDotH.cs.tmp','w')

fout.write( 'using System;\n' )
fout.write( 'namespace xrisk { class MessagesDotH {\n' )

convert_h_to_cs(fin, fout)

fout.write( '}}' )

fout.close()

s1 = open('..\user_setup\MessagesDotH.cs.tmp').read()

s2 = open('..\user_setup\MessagesDotH.cs').read()

if s1 != s2:
    os.chmod('..\user_setup\MessagesDotH.cs', stat.S_IWRITE)
    print 'deleting old MessagesDotH.cs'
    os.remove('..\user_setup\MessagesDotH.cs')
    print 'remaming tmp to MessagesDotH.cs'
    os.rename('..\user_setup\MessagesDotH.cs.tmp','..\user_setup\MessagesDotH.cs')
else:
    print 'no differences.  using same MessagesDotH.cs'

回答by gbjbaanb

I would consider using a .tt file to process the .h and turn it into a .cs file. Its very easy and the source files will then be part of your C# solution (meaning they will be refreshed as the .h file changes), can be clicked on to open in the editor, etc.

我会考虑使用 .tt 文件来处理 .h 并将其转换为 .cs 文件。它非常简单,然后源文件将成为您的 C# 解决方案的一部分(意味着它们将在 .h 文件更改时刷新),可以单击以在编辑器中打开等。

If you've only got 1 #define it might be a little overkill, but if you have a file full of them (eg a mfc resource.h file perhaps) then this solution becomes a big win.

如果你只有 1 个 #define,它可能有点矫枉过正,但如果你有一个充满它们的文件(例如 mfc resource.h 文件),那么这个解决方案就大获全胜了。

eg: create a file, DefineConverter.tt and add it to your project, change the marked line to refer to your .h file, and you'll get a new class in your project full of static const entries. (note the input file is relative to your project file, set hostspecific=false if you want absolute paths).

例如:创建一个文件 DefineConverter.tt 并将其添加到您的项目中,更改标记的行以引用您的 .h 文件,您将在项目中获得一个充满静态常量条目的新类。(注意输入文件是相对于您的项目文件,如果您想要绝对路径,请设置 hostspecific=false)。

<#@ template language="C#v3.5" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

<#
string input_file = this.Host.ResolvePath("resource.h");             <---- change this
StreamReader defines = new StreamReader(input_file);
#>
//------------------------------------------------------------------------------
//     This code was generated by template for T4
//     Generated at <#=DateTime.Now#>
//------------------------------------------------------------------------------

namespace Constants
{
    public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#>
    {
<#
    // constants definitions

    while (defines.Peek() >= 0)
    {
        string def = defines.ReadLine();
        string[] parts;
        if (def.Length > 3 && def.StartsWith("#define"))
        {
            parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
            try {
                Int32 numval = Convert.ToInt32(parts[2]);
                #>
        public static const int <#=parts[1]#> = <#=parts[2]#>;
<#
            }
            catch (FormatException e) {
            #>
        public static const string <#=parts[1]#> = "<#=parts[2]#>";
<#
            }
        }
    } #> 
    }
}

回答by JKoplo

Building on gbjbaanb's solution, I created a .tt file that finds all .h files in a specific directory and rolls them into a .cs file with multiple classes.

基于 gbjbaanb 的解决方案,我创建了一个 .tt 文件,该文件在特定目录中查找所有 .h 文件并将它们滚动到具有多个类的 .cs 文件中。

Differences

差异

  • I added support for doubles
  • Switched from try-catch to TryParse
  • Reads multiple .h files
  • Uses 'readonly' instead of 'const'
  • Trims #define lines that end in ;
  • Namespace is set based on .tt location in project
  • 我添加了对双打的支持
  • 从 try-catch 切换到 TryParse
  • 读取多个 .h 文件
  • 使用 'readonly' 而不是 'const'
  • 修剪以;结尾的 #define 行
  • 命名空间基于项目中的 .tt 位置设置


<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#
string hPath = Host.ResolveAssemblyReference("$(ProjectDir)") + "ProgramData\DeltaTau\";  
string[] hFiles = System.IO.Directory.GetFiles(hPath, "*.h", System.IO.SearchOption.AllDirectories);
var namespaceName = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint");
#>
//------------------------------------------------------------------------------
//     This code was generated by template for T4
//     Generated at <#=DateTime.Now#>
//------------------------------------------------------------------------------

namespace <#=namespaceName#>
{
<#foreach (string input_file in hFiles)
{
StreamReader defines = new StreamReader(input_file);
#>
    public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#>
    {
<#    // constants definitions

    while (defines.Peek() >= 0)
    {
        string def = defines.ReadLine();
        string[] parts;
        if (def.Length > 3 && def.StartsWith("#define"))
        {
            def = def.TrimEnd(';');
            parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries);
            Int32 intVal;
            double dblVal;
            if (Int32.TryParse(parts[2], out intVal))
            {
            #>
        public static readonly int <#=parts[1]#> = <#=parts[2]#>;           
<#
            }
            else if (Double.TryParse(parts[2], out dblVal))
            {
            #>
        public static readonly double <#=parts[1]#> = <#=parts[2]#>;            
<#
            }
            else
            {
            #>
        public static readonly string <#=parts[1]#> = "<#=parts[2]#>";
<#          
            }
        }
    } #>
    }
<#}#>     
}