知らないかもしれない便利な属性

こんにちは、.NETの世界ではほとんど使用されていないが非常に便利な属性について説明します。



それでは、次のことについて話しましょう。





InternalsVisibleToAttribute


なぜ必要なのですか? この素晴らしい属性は、ある意味でC ++ friendキーワードの遠い親relativeです。 これにより、信頼できるアセンブリがこのアセンブリの内部型および内部型メンバーにアクセスできます。 これはかなり古い属性であり、.NET 2.0で再び追加されました。



いつ便利になりますか? たとえば、これを使用して、内部タイプの単体テストを作成できます(デフォルトで内部シールドとしてマークされたクラスを作成しますか?)



例:内部型DefaultTimeScalingStrategyを含むDomain.dllアセンブリがあるとします。 さらに、Unit.Tests.dll単体テストを含むアセンブリがあります。 DefaultTimeScalingStrategyタイプの単体テストを追加するとします。 次に、Domain.dllのアセンブリを次の属性でマークする必要があります。



[assembly: InternalsVisibleTo("Domain.Tests")]
      
      







注:アセンブリDomain.Tests.dllに厳密な名前がある場合は、アセンブリの名前だけでなく、公開キー(トークンではなく)も使用する必要があります。



TypeForwardedToAttribute


なぜ必要なのですか? 型が1つのアセンブリから別のアセンブリに転送されたことを示し、型の現在のコンシューマーがクライアントを再コンパイルせずにそれを使用しないようにします。 一般的に言えば、この属性はCLRでの型転送の実装の一部であり、たとえばここで読むことができます



いつ便利になりますか? 需要のあるアセンブリ(Crypto.dll v1.0.0.0)を開発しているとします。 過去の決断を再考することもあると思います。 たとえば、タイプ(BlumBlumShub)を別のアセンブリ(PRNG.dll)に移動することを計画していますか。

次の2つの方法があります。

  1. アセンブリの新しいバージョン(Crypto.dll v1.1.0.0)をリリースするだけで、新しいアセンブリ(PRNG.dll)を参照するようになります。 ただし、ユーザーはこのタイプを使用するアプリケーション(BlumBlumShub)を再コンパイルする必要があります。これは、このタイプが移動され、CLRが現在どこを探すべきかわからないためです。
  2. アセンブリの新しいバージョン(Crypto.dll v1.1.0.0)をリリースします。これは、新しいアセンブリ(PRNG.dll)を参照し、 Typeの新しい場所に関するTypeForwardedToAttribute属性を使用して、CLRにヒントを与えます。




例:タイプ(BlumBlumShub)を別のアセンブリ(PRNG.dll)に転送するアセンブリ(Crypto.dll)があるとします。 次に、Cryptoのアセンブリを次のようにマークするだけで十分です。



 [assembly: TypeForwardedTo(typeof(BlumBlumShub))]
      
      







そして、実際には、型を転送します。



備考:

  1. ヒーローには、 TypeForwardedFromAttributeという拮抗薬があります
  2. サブジェクトを使用する実際の例は.NET 4.0自体です。Action<、>タイプが犠牲になりました。
  3. 興味深いことに、 TypeForwardedFromAttributeは通常のカスタム属性でなく、疑似カスタム属性です。 疑似属性の詳細については、 こちらをご覧ください
  4. 主題の仕事のより詳細な例はここで見つけることができます




CallerMemberNameAttribute


なぜ必要なのですか? コードを呼び出したメソッドの名前を取得できます。



いつ便利になりますか? この属性は、次の2つの場合に役立ちます。

  1. コードのトレースとデバッグ
  2. INotifyPropertyChangedインターフェイスの実装




例:次の簡単なコードを使用すると、リファクタリング中に血液を台無しにするINotifyPropertyChangedインターフェイスを実装するときにマジックストリングから身を守ることができます。

以前のように:



  internal sealed class Star : INotifyPropertyChanged { private int _luminosity; public int Luminosity { get { return _luminosity; } set { if (value != _luminosity) { _luminosity = value; OnPropertyChanged("Luminosity"); } } } private void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } }
      
      







今のまま:



 internal sealed class Star : INotifyPropertyChanged { private int _luminosity; public int Luminosity { get { return _luminosity; } set { if (value != _luminosity) { _luminosity = value; OnPropertyChanged(); } } } private void OnPropertyChanged([CallerMemberName]string propertyName = null) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; }
      
      







OnPropertyChangedメソッドの定義と呼び出しに注意してください。



備考:

  1. この件には1つの欠点があります。それは.NET 4.5でのみ登場しました。 .NET 4.0(特定のパッチをインストールする場合)で使用できるようですが、以前のバージョンでは使用できません。
  2. この属性には、 CallerFilePathAttributeCallerLineNumberAttributeの 2つの兄弟もあります。 私は彼らの名前が彼ら自身のために話すことを願っています




MethodImplAttribute


なぜ必要なのですか? この属性は、特定のメソッドの実装の機能を定義します。 メソッドコードを最適化すべきでないことをコンパイラに伝えることから、1つのスレッドのみでメソッドコードを実行することまで、多くのことができます。 このすべては、 MethodImplOptions列挙から正しいビットフラグを選択することで管理できます。





いつ使用できないのですか? 率直に言って、この列挙から1つのフラグ、つまりSynchronizedのみを使用しました。 しかし、私はあなたにそれを使用することに対して警告したいと思います。 賢い叔父のJ.リヒターは、彼のすばらしい本の 1つで、このフラグを使用するとスレッドが相互にブロックされる可能性がある例を説明しています。

要するに、 Synchronizedフラグと、たとえば次のメソッドの場合:



 public void Foo() { // Do something }
      
      





コンパイラは次のコードのようなものを生成します:

 public void Foo() { lock(this) { // Do something } }
      
      







別のスレッドが同じthisオブジェクトをロックしようとすると、これは困難になります。 したがって、過剰を避けるために、 Synchronizedを使用する代わりに、参照タイプのprivateフィールドにロックを適用すると、すべてがうまくいきます。



例:



  public static class MathUtility { [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] public static int GetFibonacciNumber(int n) { return (int)((Math.Pow((1 + Math.Sqrt(5))/2, n) - Math.Pow((1 - Math.Sqrt(5))/2, n))/Math.Sqrt(5)); } }
      
      







注: TypeIwardAttributeToなどのMethodImplAttribute属性は、擬似属性です。 さらに、メソッドに適用されたかどうかを確認するために、特別なAPI、つまりMethodInfoクラスのGetMethodImplementationFlagsメソッドがあります。



伝えたかったのはそれだけです。

ご質問がある場合は、私の能力と知識を最大限に活用して回答させていただきます。



All Articles