DynamicProxyとカスタム属性

クラスFoobarに対してカスタム属性"HogeAttribute"を設定し、そのクラスFoobarをDynamicProxyで拡張するとしよう。

[Hoge(〜       )]
public class Foobar
{
}
〜
Foobar proxy = this.generator.CreateClassProxy(typeOf(Foobar), this, new object[] { null});

このとき、プロキシにより拡張された型でカスタム属性HogeAttributeを取得するためには、HogeAttributeクラスのAttributeUsage属性に"Inherited=true"が指定されていなくてはならないことは以前のエントリで紹介した。
[カスタム属性のInheritedプロパティ(その2)]
ここまでは良いのだが、カスタム属性によっては継承したくないケースがあるのだ。例えば拙作のフレームワークではビジネスロジックを呼び出すアクションクラスを種々のGUIに結びつけるためのカスタム属性があるのだが、これなどはクラスと併せてカスタム属性も継承してしまうとGUIとの結びつけが二重になってしまうのである。

[ContributeGUI(contributeType = ContributeType.Menu, Sibling="file")]
public class FooAction
{
}
〜
[ContributeGUI(contributeType = ContributeType.Menu, Sibling="file")]
public class ExtendsFooAction : FooAction
{
}
〜
//インスタンスを生成するのはExtendsFooAction だけのはずだが、継承されたカスタム属性により、メニュー"file"配下にはFooActionとExtendsFooActionの両方のインスタンスが結び付けられてしまう。
ExtendsFooAction action = new ExtendsFooAction();
ContributeGUIAttribute attr = Attribute.GetCustomAttribute(〜

これを防ぐには簡単で、この例で言えばContributeGUIAttributeクラスに設定するAttributeUsage属性のInheritedプロパティfalseに設定すればよいのだが、

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class ContributeGUIAttribute : Attribute
{
}

今度はこのカスタム属性を設定したクラスをDynamicProxyで拡張したときに、継承されて欲しいカスタム属性は、当たり前だが継承されないのである。このケースではDynamicProxyを使用する場合、カスタム属性は全て"継承可能"としておき、二重に取得されるカスタム属性をまとめる工夫が必要だろう。
動的に型を拡張する方法を採ることで、拡張元の型との互換性が最大限に保たれるDynamicProxyだが、型を拡張しているということはこのような副作用が発生することも考慮しなくてはならない。