5

ここで、C# の詳細を学ぶために時間を費やしていたので、カスタム属性を調べることにしました。Enum に割り当てると非常に役立つことがわかりました。

そのため、Enum がそのような属性を非常に簡単に取得できるように、次のようないくつかの拡張メソッドを作成しましたDemoEnum.Value1.GetAttribute<EnumNote>()

しばらくして、すべてのカスタム属性が、割り当てられた Enum への参照を持っていればいいと思いました。私が考えた悪い考えではないので、私はこれを進めました:

最初に、もちろんクラスEnumAttributeを継承するカスタム属性の基本クラスを作成しました。System.Attributeこの基本クラスは最初のスケッチに過ぎず、受信する Enum のすべてのタイプに特に適合するように拡張するつもりですが、今のところこれで十分です。

public class EnumAttribute : Attribute
{
    public EnumInfo Enum { get; internal set; }

    public EnumAttribute(Enum Enum)
    {
        this.Enum = new EnumInfo(Enum);
    }

    public class EnumInfo
    {
        private Enum _value;
        private Type _type;
        private FieldInfo _details;

        public Enum Value { get { return _value; } }
        public Type Type { get { return _type; } }
        public FieldInfo Details { get { return _details; } }

        public EnumInfo(Enum value)
        {
        _value = value;
        _type = value.GetType();
        _details = _type.GetField(System.Enum.GetName(_type, value));
        }
    }
}

すべてのカスタム属性は、このクラスから継承する必要がありEnumAttributeます。たとえば、次のようなクラスになりEnumNoteます。

public class EnumNote : EnumAttribute
{
    private string _note = string.Empty;

    public string Note { get { return _note; } }

    public EnumNote(Enum Enum, string Note)
        : base(Enum)
    {
        _note = Note;
    }
}

これまでのところすべて問題なく、Visual Studio のコード分析とコンパイラは何も報告しません。

しかし、次のように Enum を定義すると、次のようになります。

public enum DemoEnum
{
    [EnumNote(DemoEnum.Value1, "Some special note about Enum Value1.")]
    Value1 = 1,

    [EnumNote(DemoEnum.Value2, "Some other special note about Enum Value2.")]
    Value2 = 2
}

コンパイルしようとすると、VS は各EnumNoteコンストラクターの最初の引数について次のように報告します。

属性引数は、定数式、typeof 式、または属性パラメーター タイプの配列作成式である必要があります。

基本的に、DemoEnum.Value1 と DemoEnum.Value2 は定数値を保持しないと言っています。私はそうですよね?

いずれにせよ、この Enum はハードコードされているため、このエラーには困惑しています。ご覧のとおり、既に自分で行っているため、コンパイラは各列挙型に値を割り当てる必要さえありません。

もちろん、これは私が欠けているものや誤解しているもの、そしてそれぞれEnumNoteが割り当てられている列挙型への参照を提供するという目標を達成するにはどうすればよいのでしょうか?

ありがとうございました。

アップデート:

それを再確認した後、VSが列挙型が定数式ではないことを報告する理由を理解しました。それは、私が何かを参照しているためDemoEnum.Value1ですDemoEnumValue1場所。しかし、私のこのアイデアをどのように進めるかについてのアイデアはありません。

更新 2:

コンパイル後に列挙型をリファクタリングすることについてはどうですか。列挙型に適用できない場合を除き(リファクタリングが列挙型で機能するかどうか思い出せません)、おそらくエラーは発生しないはずですが、適用できる場合はおそらく面倒で遅くなりますよね?

更新 3:属性に割り当てられた Enum への参照が含まれている理由。

実際には非常に単純な理由です。次のシナリオを想定してください。カスタム属性のコレクションがあり、何らかの理由で、ある時点で、特定の属性がどの Enum に属しているかを知る必要があるとします。

それを判断するために新しいコードを書く代わりに、属性自体で特定の Enum を参照するだけではどうですか? これは消費されるメモリのわずかな妥協ですが、将来的には、各属性 (または 1 つだけ) の指定された Enum を決定するために必要なプロセスを実行するための貴重な時間を節約できます。

4

2 に答える 2

11

属性引数は、定数式、typeof 式、または属性パラメーター タイプの配列作成式である必要があります。

基本的に、DemoEnum.Value1 と DemoEnum.Value2 は定数値を保持しないと言っています。私はそうですよね?

そうではありません。

実際の問題は、の使用にあるようですEnumほとんどが概念的なクラス階層の基本クラスとして機能しますが、少し巧妙に処理されているようです。すべての意図と目的において、これは真のクラスではなく、実行時の疑似クラスです。

属性はコンパイル時に処理されます。実行時にオブジェクトを属性で装飾するメカニズムはありません。Enumそのため、疑似クラスを処理するための実行時のちょっとしたトリックの導入は、属性の機能方法と互換性がないように見えます。

これを試して:

public class EnumTest : Attribute
{
    public int Value;
    public object Obj;
}

public enum DemoEnum
{
    [EnumTest(Value = (int)DemoEnum.Value1, Obj = DemoEnum.Value1)]
    Value1 = 1,

    [EnumTest(Value = (int)DemoEnum.Value2)]
    Value2 = 2
}

これは正常にコンパイルされ (VS2010 の .NET 4.0)、属性プロパティが正しく設定されます。ObjAttributeのプロパティを調べて、期待どおりの種類のデータを取得できます。

<意見>
そうは言っても...これがどれほど役立つかはよくわかりません。装飾している値のインスタンスを既に持っている必要があるため、値自体を Attribute に格納するのは少し冗長に思えます。
</意見>

編集:可能な解決策...

これを少し浸透させた後(睡眠中に最善を尽くして考えます)、問題は実際のパラメーターの型にあるため(この質問への回答で述べたように)、問題を少し回避できることに気付きましたボクシングの。

public class EnumTest : Attribute
{
    public Enum enum;

    public EnumTest(object e)
    {
        enum = e as Enum;
    }
}

このようにして、オブジェクトから Attribute を初期化しています。これは、C# と CLR が非常に快適に行うように見えます。Enum での初期化に固有の型チェックは失われますが、記載されている目的は達成されると思います。

于 2013-02-14T05:27:47.983 に答える
1

コンパイル時の値で非定数属性コンストラクターに渡すことはできません。属性制限です。だからあなただけができる

[EnumNote(1,"Some special note")]
Value1=1,

しかし、あなたの目的は、C# でアスペクト指向プログラミングを混合している属性のアイデアを壊しました。したがって、属性を使用しないでください。

この記事を読むことができる属性とアスペクト指向プログラミング について

于 2013-02-14T04:50:59.793 に答える