リフレクション中に、あるコンストラクターが別のコンストラクターを呼び出すかどうかを C# で確認することは可能ですか?
class Test
{
public Test() : this( false ) { }
public Test( bool inner ) { }
}
ConstructorInfo
呼び出しの連鎖の最後にあるかどうかをそれぞれ判断したいと思います。
リフレクション中に、あるコンストラクターが別のコンストラクターを呼び出すかどうかを C# で確認することは可能ですか?
class Test
{
public Test() : this( false ) { }
public Test( bool inner ) { }
}
ConstructorInfo
呼び出しの連鎖の最後にあるかどうかをそれぞれ判断したいと思います。
これは、これまでに見つけたことを述べるための一時的な回答です。
ConstructorInfo
コンストラクターが別のコンストラクターを呼び出すかどうかを示すプロパティは見つかりませんでした。のプロパティもそうではありませんでしたMethodBody
。
MSIL バイト コードの評価にある程度成功しています。OpCodes.Call
私の最初の調査結果は、最終的に呼び出されるコンストラクターが、いくつかの可能な other を除いて、すぐに始まることを示していOpCodes
ます。他のコンストラクターを呼び出すコンストラクターには 'unexpected' がありOpCodes
ます。
public static bool CallsOtherConstructor( this ConstructorInfo constructor )
{
MethodBody body = constructor.GetMethodBody();
if ( body == null )
{
throw new ArgumentException( "Constructors are expected to always contain byte code." );
}
// Constructors at the end of the invocation chain start with 'call' immediately.
var untilCall = body.GetILAsByteArray().TakeWhile( b => b != OpCodes.Call.Value );
return !untilCall.All( b =>
b == OpCodes.Nop.Value || // Never encountered, but my intuition tells me a no-op would be valid.
b == OpCodes.Ldarg_0.Value || // Seems to always precede Call immediately.
b == OpCodes.Ldarg_1.Value // Seems to be added when calling base constructor.
);
}
MSILについてはまったくわかりません。おそらく、その間に何もしないことは不可能であるか、そのようなコンストラクターを開始する必要はまったくありませんが、現在のすべての単体テストでは機能しているようです。
[TestClass]
public class ConstructorInfoExtensionsTest
{
class PublicConstructors
{
// First
public PublicConstructors() : this( true ) {}
// Second
public PublicConstructors( bool one ) : this( true, true ) {}
// Final
public PublicConstructors( bool one, bool two ) {}
// Alternate final
public PublicConstructors( bool one, bool two, bool three ) {}
}
class PrivateConstructors
{
// First
PrivateConstructors() : this( true ) {}
// Second
PrivateConstructors( bool one ) : this( true, true ) {}
// Final
PrivateConstructors( bool one, bool two ) {}
// Alternate final
PrivateConstructors( bool one, bool two, bool three ) {}
}
class TripleBaseConstructors : DoubleBaseConstructors
{
public TripleBaseConstructors() : base() { }
public TripleBaseConstructors( bool one ) : base( one ) { }
}
class DoubleBaseConstructors : BaseConstructors
{
public DoubleBaseConstructors() : base() {}
public DoubleBaseConstructors( bool one ) : base( one ) {}
}
class BaseConstructors : Base
{
public BaseConstructors() : base() {}
public BaseConstructors( bool one ) : base( one ) {}
}
class Base
{
// No parameters
public Base() {}
// One parameter
public Base( bool one ) {}
}
class ContentConstructor
{
public ContentConstructor()
{
SomeMethod();
}
public ContentConstructor( bool one )
{
int bleh = 0;
}
bool setTwo;
public ContentConstructor( bool one, bool two )
{
setTwo = two;
}
void SomeMethod() {}
}
[TestMethod]
public void CallsOtherConstructorTest()
{
Action<ConstructorInfo[]> checkConstructors = cs =>
{
ConstructorInfo first = cs.Where( c => c.GetParameters().Count() == 0 ).First();
Assert.IsTrue( first.CallsOtherConstructor() );
ConstructorInfo second = cs.Where( c => c.GetParameters().Count() == 1 ).First();
Assert.IsTrue( second.CallsOtherConstructor() );
ConstructorInfo final = cs.Where( c => c.GetParameters().Count() == 2 ).First();
Assert.IsFalse( final.CallsOtherConstructor() );
ConstructorInfo alternateFinal = cs.Where( c => c.GetParameters().Count() == 3 ).First();
Assert.IsFalse( alternateFinal.CallsOtherConstructor() );
};
// Public and private constructors.
checkConstructors( typeof( PublicConstructors ).GetConstructors() );
checkConstructors( typeof( PrivateConstructors ).GetConstructors( BindingFlags.NonPublic | BindingFlags.Instance ) );
// Inheritance.
Action<ConstructorInfo[]> checkBaseConstructors = cs =>
{
ConstructorInfo noParameters = cs.Where( c => c.GetParameters().Count() == 0 ).First();
ConstructorInfo oneParameter = cs.Where( c => c.GetParameters().Count() == 1 ).First();
// Only interested in constructors specified on this type, not base constructors,
// thus calling a base constructor shouldn't qualify as 'true'.
Assert.IsFalse( noParameters.CallsOtherConstructor() );
Assert.IsFalse( oneParameter.CallsOtherConstructor() );
};
checkBaseConstructors( typeof( BaseConstructors ).GetConstructors() );
checkBaseConstructors( typeof( DoubleBaseConstructors ).GetConstructors() );
checkBaseConstructors( typeof( TripleBaseConstructors ).GetConstructors() );
// Constructor with content.
foreach( var constructor in typeof( ContentConstructor ).GetConstructors() )
{
Assert.IsFalse( constructor.CallsOtherConstructor() );
}
}
}
Cecilは、Reflectionと同様に、コンパイルされたアセンブリで動作します。SharpDevelop IDEでのリファクタリングをサポートするために、その上に高レベルのライブラリが構築されているため、これを簡単にするための何かがあるかもしれません。
Roslynはソースコードを操作し、それに基づいたオブジェクトモデルを提供します。そのため、バイナリではなくソースに対して作業する場合は、作業がさらに簡単になる可能性があります。
(私は実際にこのような目的でCecilを使用したことはなく、Roslynをまったく使用したことがないので、プロジェクトを紹介して幸運を祈る以上のことはできません。それがどのように進んだか聞いてみたいです!)
できることは、アスペクトが適用されたことを示すプロパティをオブジェクトに追加することです。したがって、そのプロパティを確認できるため、アスペクトを何度も適用することはありません。それはあなたが尋ねたものではありませんが、あなたの根底にある問題を解決するのに役立つかもしれません.
私の知る限り、簡単にリフレクションを使用してコードをチェックまたは検査することはできません。リフレクションでできることは、アセンブリのメタデータ情報を反映することだけです。
GetMethodBodyを使用してメソッドのコンテンツを取得できますが、実際に解析して IL を自分で理解する必要があります。