检查构造函数是否调用另一个构造函数
本文关键字:构造函数 另一个 调用 是否 检查 | 更新日期: 2023-09-27 18:28:47
在反射过程中,C#是否可以检查一个构造函数是否调用另一个构造函数?
class Test
{
public Test() : this( false ) { }
public Test( bool inner ) { }
}
我想确定每个ConstructorInfo
是否处于调用链的末尾。
这是一个临时答案,用来说明我到目前为止的发现。
我没有发现ConstructorInfo
的任何属性可以指示构造函数是否调用另一个构造函数。CCD_ 3的性质也不一样。
我在评估MSIL字节码方面取得了一定的成功。我的第一个发现表明,最终被调用的构造函数立即从OpCodes.Call
开始,除了一些可能的其他OpCodes
。调用其他构造函数的构造函数具有"意外"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或Roslyn。
Cecil像Reflection一样对编译后的程序集进行操作。它在上面构建了更高级别的库,以支持SharpDevelop IDE中的重构,所以它可能有一些东西可以让这更容易。
Roslyn对源代码进行操作,并在此基础上为您提供了一个对象模型,因此,如果您愿意针对源代码而不是二进制代码进行工作,那么使用它可能会更容易。
(我从来没有真正使用过Cecil,也从来没有使用过Roslyn,所以我只能向你介绍项目并祝你好运。如果你真的成功了,我很想听听进展如何!)
您可以做的是向对象添加一个属性,告诉它应用了方面。因此,您不会多次应用方面,因为您可以检查该属性。这不是你所要求的,但它可能会帮助你解决根本问题。
据我所知,您无法以简单的方式使用反射来检查或检查代码。反射所能做的就是反射程序集的元数据信息。
您可以使用GetMethodBody获取方法的内容,但随后您必须实际解析它并自己理解IL。