Проверьте, вызывает ли конструктор другой конструктор - PullRequest
5 голосов
/ 24 марта 2012

Во время отражения, возможно ли в C # проверить, вызывает ли один конструктор другой?

class Test
{
    public Test() : this( false ) { }
    public Test( bool inner ) { }    
}

Я хотел бы определить для каждого ConstructorInfo, находится ли он наконец цепочки вызова.

Ответы [ 4 ]

3 голосов
/ 24 марта 2012

Это временный ответ, чтобы заявить, что я нашел до сих пор.

Я не нашел ни одного свойства ConstructorInfo, которое могло бы указать, вызывает ли конструктор другой конструктор или нет. Также не было свойств MethodBody.

У меня есть некоторые успехи при оценке байтового кода 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. Возможно, между ними невозможно сделать no-ops, или совсем не нужно запускать такой конструктор, но для всех моих текущих модульных тестов это работает.

[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() );
        }               
    }
}
1 голос
/ 25 марта 2012

Посмотрите на Сесил или Рослин .

Сесил работает с скомпилированной сборкой, как Reflection. поверх него встроены библиотеки более высокого уровня для поддержки рефакторингов в IDE SharpDevelop, так что, возможно, есть что-то, чтобы упростить это.

Roslyn работает с исходным кодом и предоставляет вам объектную модель, основанную на этом, поэтому, если вы хотите работать с исходным кодом, а не с двоичными файлами, работать с ним может быть еще проще.

(Я никогда не использовал Сесил для чего-то подобного, и я никогда не использовал Рослин вообще, так что я не могу сделать больше, чем просто указать вам на проекты и пожелать вам удачи. Если вам удастся получить что-то работает, мне было бы интересно услышать, как все прошло!)

1 голос
/ 24 марта 2012

что вы можете сделать, это добавить свойство к объекту, сообщающее, что аспект был применен. Таким образом, вы не будете применять аспект несколько раз, так как можете проверить это свойство. Это не то, что вы просили, но это может помочь вам в вашей основной проблеме.

0 голосов
/ 24 марта 2012

Насколько я знаю, вы не можете легко проверять или проверять код, используя отражение.Все, что вам позволяет сделать рефлексия, это отражать информацию метаданных сборки.

Вы можете использовать GetMethodBody , чтобы получить содержимое метода, но тогда вам придется проанализировать его и понятьИл сам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...