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

ネストされた各クラスの静的コンストラクターは、ネストされたクラスが初めて参照されるまで実行されません。上記のコードでは発生していないため、リストは空です。

これを行うことができると想像できる1つの方法は、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はここでも役立つ場合があります。「ライトバージョン」を実装する代わりに、MEFの使用を検討することをお勧めします。静的コンストラクター登録でさえ、コンパイル時ではなく実行時登録であるため、それを避けた理由がわかりません。

于 2012-10-05T11:09:14.197 に答える