是否可以在没有项目设置的情况下在C#代码中#define定义一个常量解决方案? [英] Can I #define a constant solutionwide within c# code without project settings?
问题描述
我知道这很奇怪,并且回答了几次,例如 全解决方案的#define ,如何在C#中全局定义常量(如DEBUG).
I know this was aksed and answered a a couple of times e.g. Solution-wide #define, Is There anyway to #define Constant on a Solution Basis? and How to define a constant globally in C# (like DEBUG).
但就我而言,我无法使用任何建议的方法:
But in my case I can not use any of the suggested methods:
我正在为UnityProjects(一种提供某种功能的程序包)编写不同的模块"(或插件,如果需要的话).这个想法是,开发人员可以通过导入其中包含所有脚本和资源的UnityPackage
来加载某个模块"以在其项目中使用.
I'm writing on different "modules" (or plugins if you want so) for UnityProjects (kind of a package providing a certain functionality). The idea is that a developer can load a certain "module" to use in his project by importing a UnityPackage
with all scripts and resources in it.
但是其中一些模块本身依赖于其他模块.所以到目前为止,我尝试的是在每个模块中都有一个Constants
类,该类具有单独的名称空间和预处理器定义.
But some of these modules themselves depend on other modules. So what I tried so far was having a class Constants
in each module with seperated namespaces and preprocessor definitions.
模块A
#if !MODULE_A
#define MODULE_A // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif
namespace Module_A
{
public static class Constants
{
// some constants for this namespace here
}
}
模块B
#if !MODULE_B
#define MODULE_B // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif
#if !MODULE_A // WILL BE NOT DEFINED OFCOURSE SINCE #define IS NOT GLOBAL
#error Module A missing!
#else
namespace Module_B
{
public static class Constants
{
// some constants for this namespace here
}
// and other code that might require Module A
}
#endif
但是,这当然不能像这样工作,因为#define
不是全局的,而是仅在当前文件中.
But ofcourse this cannot work like this since #define
s are not global but only in the current file.
对于模块的整个概念和简单的加载模块",我不能要求用户首先对项目或解决方案设置进行更改,例如由此答案建议,但必须仅使用UnityPackage随附的(c#)资源(至少与我的当前的专有技术).
For this whole idea of modules and a simple "load your modules" I can not ask the user to first make changes to the project or solution settings how e.g. suggested by this answer but instead have to use only the (c#) resources that come imported with the UnityPackage (at least with my current know-how).
有什么方法可以通过仅导入模块的UnityPackage为整个Unity-Project设置/定义这些常量吗?
我可以使用资产来在Unity中为 1 定义找到解决方案/msc.rsp .但这仍然不适用于多个模块,因为它们必须写入同一文件.
I could find a solution for 1 definition in Unity using Assets/msc.rsp. But this still wouldn't work for multiple modules since they would have to write into the same file.
推荐答案
经过大量搜索,我终于能够提出一个令人惊讶的简单解决方案,我想与您分享:
InitializeOnLoad
Unity具有属性 [InitializeOnLoad]
.它告诉Unity尽快根据
Unity has an attribute [InitializeOnLoad]
. It tells Unity to initialize according class as soon as
- 团结开始
- 重新编译脚本后=>以及使用脚本导入新的unitypackage之后
静态构造函数
在他们的在启动时运行编辑器代码的示例中,他们展示了如何组合使用static
构造函数.
In their Running Editor Code On Launch example, they show, how to combine this with a static
constructor.
来自静态-构造函数:
在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类.
A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.
通常您仍然需要创建该类的实例,但是在初始化该类时,静态构造函数会立即实例化/执行",这是我们使用[InitializeOnLoad]
属性强制执行的.
While usually you still would have to create an instance of the class, the static constructor is "instanciated/executed" instantly when the class is initliazed, which we force using the [InitializeOnLoad]
attribute.
脚本定义符号
进一步的Unity实际上在PlayerSettings
中已经在项目范围内定义.
Further Unity actually has project wide defines in the PlayerSettings
.
好的是:我们还可以通过脚本API访问它们:
And the good part is: We also have access to them via scripting API:
所以我现在要做的是以下
So what I did now is the following
该模块没有依赖性,只是在PlayerSettings中定义了一个全局定义".我将此脚本放置在例如在Assets/ModuleA/Editor
中(重要的是最后一个文件夹的名称).
This module has no dependencies but just defines a "global define" in the PlayerSettings. I placed this script somewhere e.g. in Assets/ModuleA/Editor
(important is the last folder's name).
using System.Linq;
using UnityEditor;
namespace ModuleA
{
// Will be initialized on load or recompiling
[InitializeOnLoad]
public static class Startup
{
// static constructor is called as soon as class is initialized
static Startup()
{
#region Add Compiler Define
// Get the current defines
// returns a string like "DEFINE_1;DEFINE_2;DEFINE_3"
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// split into list just to check if my define is already there
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_A")
{
// if not there already add my define
defines += ";MODULE_A";
}
// and write back the new defines
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endregion
}
}
}
模块B
此模块取决于Module A
.因此,它本身定义了一个全局定义"(以便以后的模块可以检查它们在Module B
上的依赖关系),但是另外,它还会首先检查是否导入了模块A.如果缺少Module A
,它将向调试控制台输出错误.
Module B
This module depends on Module A
. So itself defines a "global define" (so later Modules can check their dependecies on Module B
) but additionally it checks first, if Module A is imported. If Module A
is missing, it prints an error to the Debug Console.
(您也可以使用#error SOME TEXT
引发编译器错误,但是由于某些原因,它无法正确打印出URL,因此我决定使用Debug.LogError
)
(You could as well throw a compiler error using #error SOME TEXT
, but for some reason this is not capable of printing out the URL correctly so I decided for the Debug.LogError
)
我将此脚本放置在例如在Assets/ModuleB/Editor
I placed this script somewhere e.g. in Assets/ModuleB/Editor
#if MODULE_A
using System.Linq;
#endif
using UnityEditor;
#if !MODULE_A
using UnityEngine;
#endif
namespace ModuleB
{
// Will be initialized on load or recompiling
[InitializeOnLoad]
public static class Startup
{
// static constructor is called as soon as class is initialized
static Startup()
{
#if !MODULE_A
Debug.LogErrorFormat("! Missing Module Dependency !" +
"\nThe module {0} depends on the module {1}." +
"\n\nDownload it from {2} \n",
"MODULE_B",
"MODULE_A",
"https://Some.page.where./to.find.it/MyModules/ModuleA.unitypackage"
);
#else
// Add Compiler Define
var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
var define = defines.Split(';').ToList();
if (!define.Contains("MODULE_B"))
{
defines += ";MODULE_B";
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);
#endif
}
}
}
因此,稍后在 B模块的其他脚本中,我有两个选择(两者基本相同)
So later in other scripts of Module B I have two options (both do basically the same)
-
我可以检查所有地方
#if MODULE_A
来准确检查此脚本所依赖的模块
I can either check everywhere
#if MODULE_A
to check exactly the module this script relies on
或我可以改为检查#if MODULE_B
而不用一行来检查是否满足所有的依存关系,因为否则我将不定义MODULE_B
.
or I can instead check #if MODULE_B
to rather check with one line if all dependecies are fulfilled since otherwise I don't define MODULE_B
.
通过这种方式,我可以完全检查某些模块之间的所有依赖关系,这是很棒的.到目前为止,我看到的仅有两个缺陷是:
On this way I can completely check all dependencies between certain modules which is awesome. The only two flaws I saw until now are:
- 我们必须知道每个模块的定义(例如
MODULE_A
)是什么样子,如果将来更改,则还必须在所有依赖的模块中对其进行更改 - 如果从项目中删除了模块,则不会删除全局定义"
- We have to know how the define (e.g.
MODULE_A
) looks like for every module and if it is changed in the future it has to be changed in all depending modules as well - The "global define" isn't getting removed in case the module is deleted from the project
但是-哪种解决方案是完美的?
But well - which solution is perfect?
这篇关于是否可以在没有项目设置的情况下在C#代码中#define定义一个常量解决方案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!