0

我们的控制系统有很多具有特定版本的数据文件。数据文件是“仅附加”的,因此更高版本将始终包含早期版本中定义的超集。

为了避免在我们的代码中出现“幻数”,我们创建了一个代码生成器,它获取一段数据并将其转换为 C# 类文件。我有一个非常大的类库,几乎只包含这样的文件。这会导致代码更具可读性和一些额外的编译器检查,因为以前有风险的类型转换现在可以成为生成代码的一部分,以便库的用户直接使用他们正确的子类型。

但是,问题出现在最新版本的类库中使用了什么数据文件版本。我希望每个类文件都“贡献”到一个全局列表。如果静态构造函数自动运行,我会这样做:

// GlobalConstants.cs (not generated)
public static partial class GlobalConstants
{
    public static List<string> Lst = new List<string>(); 
    static void AddVersionInfo(string mod)
    {
        Lst.Add(mod);
    }
}

// Motor.cs
public static partial class GlobalConstants
{
    // Generated from Motor metadata
    public class MotorUtil
    {            
        public const int SomeConst1 = 2;
        static MotorUtil()
        {
            AddVersionInfo("<Motor version info>");
        }
    }
}

// Tank.cs
public static partial class GlobalConstants
{
    // Generated from Tank metadata        
    public class TankUtil
    {
        public const int SomeConst1 = 2;
        public const int SomeConst2 = 3;
        public const int SomeConst3 = 5;
        static TankUtil()
        {
            AddVersionInfo("<Tank version info>");
        }
    }
}

如果静态构造函数自动运行,这将导致每个生成的文件(tank.cs、motor.cs、boiler.cs 等)都被添加到列表中,可以通过工具进行检查。

一个编译时 MEF,如果你喜欢的话。

很遗憾。如果我打印出“Lst”的内容,那么它总是一个空列表,因为静态构造函数没有运行。

这些 .cs 文件来自不同的各方,不应要求 GlobalConstants.cs 的作者了解它们。我的问题是:有没有一种方法可以生成此代码,以便可以随意添加生成的 .cs 文件,它们将出现在“Lst”列表中?

4

2 回答 2

3

在第一次引用这些嵌套类之前,每个嵌套类的静态构造函数都不会运行。上面的代码中没有发生这种情况,因此列表为空。

我可以想象您可能能够做到这一点的一种方法是让GlobalConstants类中的静态初始化程序使用反射来查找嵌套类。

或者,也许您可​​以在每个引用嵌套类的分部类文件中添加一个静态字段,强制其静态构造函数运行:

public static partial class GlobalConstants
{
    // Generated from Tank metadata        

    private static TankUtil _unusedTankUtilField = new TankUtil();

    public class TankUtil
    {
        public const int SomeConst1 = 2;
        public const int SomeConst2 = 3;
        public const int SomeConst3 = 5;
        static TankUtil()
        {
            AddVersionInfo("<Tank version info>");
        }
    }
}

另外我不相信跨部分类文件的静态初始化程序的执行顺序是严格定义的(大概对应于编译器处理它们的顺序)。所以我建议你让你的“AddVersionInfo”方法负责初始化列表:

public static partial class GlobalConstants
{
    public static List<string> Lst; 
    static void AddVersionInfo(string mod)
    {
        if (Lst == null) Lst = new List<string>();
        Lst.Add(mod);
    }
}
于 2012-10-05T10:43:16.753 回答
1

对于更简单的语法,您可以使用属性而不是静态类来完成。

如果你定义一个属性;

public class UtilVersionAttribute : Attribute
{
    private readonly string _versionInfo;

    public UtilVersionAttribute(string versionInfo) { _versionInfo = versionInfo; }

    public string VersionInfo { get { return _versionInfo; } }
}

...您可以改为将类归类...

public static partial class GlobalConstants
{
    [UtilVersion("<Motor version info>")]
    public class MotorUtil
    {
        public const int SomeConst1 = 2;
    }
}

...并在程序第一次开始使用时更新它...

static void UpdateVersionInfo(Assembly assembly)
{
    foreach (Type type in assembly.GetTypes())
    {
        var attribute = 
            type.GetCustomAttributes(typeof (UtilVersionAttribute), true)
                .FirstOrDefault();

        if(attribute != null)
            GlobalConstants.Lst.Add((attribute as UtilVersionAttribute).VersionInfo);
    }
}

static void Main(string[] args)
{
    var assembly = Assembly.GetExecutingAssembly();
    UpdateVersionInfo(assembly);

    GlobalConstants.Lst.ForEach(Console.WriteLine);
}

...查看您的版本的完整列表。

MEF 在这里也可能有所帮助,您可能需要考虑使用它而不是实现“轻量版”。即使您的静态构造函数注册也是运行时注册,而不是编译时间,所以我不确定您远离它的原因。

于 2012-10-05T11:09:14.197 回答