52

Attributeというクラスを作成しましたRelatedPropertyAttribute

[AttributeUsage(AttributeTargets.Property)]
public class RelatedPropertyAttribute: Attribute
{
    public string RelatedProperty { get; private set; }

    public RelatedPropertyAttribute(string relatedProperty)
    {
        RelatedProperty = relatedProperty;
    }
}

これを使用して、クラス内の関連するプロパティを示します。私がそれを使用する方法の例:

public class MyClass
{
    public int EmployeeID { get; set; }

    [RelatedProperty("EmployeeID")]
    public int EmployeeNumber { get; set; }
}

「魔法の文字列」ではなく、強力な型を属性のコンストラクターに渡すことができるように、ラムダ式を使用したいと思います。このようにして、コンパイラの型チェックを利用できます。例えば:

public class MyClass
{
    public int EmployeeID { get; set; }

    [RelatedProperty(x => x.EmployeeID)]
    public int EmployeeNumber { get; set; }
}

次のようにできると思っていましたが、コンパイラで許可されていません。

public RelatedPropertyAttribute<TProperty>(Expression<Func<MyClass, TProperty>> propertyExpression)
{ ... }

エラー:

非ジェネリック型 'RelatedPropertyAttribute' は型引数では使用できません

どうすればこれを達成できますか?

4

7 に答える 7

57

一般的な属性を持つことは、従来の方法では不可能です。ただし、C# と VB はサポートしていませんが、CLR はサポートしています。ILコードを書きたい場合は可能です。

あなたのコードを見てみましょう:

[AttributeUsage(AttributeTargets.Property)]
public class RelatedPropertyAttribute: Attribute
{
    public string RelatedProperty { get; private set; }

    public RelatedPropertyAttribute(string relatedProperty)
    {
       RelatedProperty = relatedProperty;
    }
}

コードをコンパイルし、ILSpyまたはILDasmでアセンブリを開き、コンテンツをテキスト ファイルにダンプします。属性クラス宣言の IL は次のようになります。

.class public auto ansi beforefieldinit RelatedPropertyAttribute
extends [mscorlib]System.Attribute

テキスト ファイルでは、属性をジェネリックにすることができます。変更する必要があることがいくつかあります。

これは IL を変更するだけで簡単に実行でき、CLR は文句を言いません。

.class public abstract auto ansi beforefieldinit
      RelatedPropertyAttribute`1<class T>
      extends [mscorlib]System.Attribute

relatedPropertyの型を文字列からジェネリック型に変更できるようになりました。

例えば:

.method public hidebysig specialname rtspecialname 
    instance void .ctor (
        string relatedProperty
    ) cil managed

次のように変更します。

.method public hidebysig specialname rtspecialname 
    instance void .ctor (
        !T relatedProperty
    ) cil managed

そのような「汚い」仕事をするためのフレームワークはたくさんあります: Mono.CecilまたはCCI

すでに述べたように、これはクリーンなオブジェクト指向ソリューションではありませんが、C# と VB の限界を打破する別の方法を指摘したかっただけです。

このトピックに関する興味深い読み物があります。この本をチェックしてください

それが役に立てば幸い。

于 2013-05-29T09:16:16.800 に答える
44

それはいけません

  • 一般的な属性タイプを作成することはできません (単に許可されていません)。同様に、汎用属性 ( ) を使用する[Foo<SomeType>]ための構文は定義されていません
  • 属性初期化子でラムダを使用することはできません - 属性に渡すことができる値は非常に限られており、単に式を含まないだけです (非常に複雑で、ランタイム オブジェクトであり、コンパイル時のリテラルではありません)。
于 2013-05-29T08:54:08.427 に答える
15

C# 6.0 を使用している場合は、nameofを使用できます。

変数、型、またはメンバーの単純な (修飾されていない) 文字列名を取得するために使用されます。コードのエラーを報告するとき、Model-View-Controller (MVC) リンクを接続するとき、プロパティ変更イベントを発生させるときなど、メソッドの文字列名を取得したいことがよくあります。nameof を使用すると、定義の名前を変更するときにコードを有効に保つことができます。以前は文字列リテラルを使用して定義を参照する必要がありましたが、ツールはこれらの文字列リテラルをチェックすることを認識していないため、コード要素の名前を変更するときに脆弱でした。

それを使用すると、次のように属性を使用できます。

public class MyClass
{
    public int EmployeeID { get; set; }

    [RelatedProperty(nameof(EmployeeID))]
    public int EmployeeNumber { get; set; }
}
于 2015-12-11T01:06:53.550 に答える