Определить сгенерированный компилятором конструктор по умолчанию, используя отражение в C # - PullRequest
4 голосов
/ 07 июля 2010

Я нацеливаюсь на .NET 3.5 SP1 и использую CommentChecker для проверки моей документации XML, все работает нормально, пока я не доберусь до такого класса:

/// <summary>
/// documentation
/// </summary>
public sealed class MyClass {
    /// <summary>
    /// documentation
    /// </summary>
    public void Method() {
    }
}

В приведенном выше примере, как я понимаю, компилятор создает конструктор по умолчанию для моего класса.Проблема в том, что CommentChecker генерирует предупреждения о том, что в конструкторе отсутствуют комментарии.

Я пытался изменить программу, чтобы обнаружить этот особый случай и игнорировать его, но я застрял, я уже пытался с IsDefined(typeof(CompilerGeneratedAttribute), true), но это не сработало.

Короче говоря, какя могу определить конструктор по умолчанию, используя отражение?

Ответы [ 3 ]

5 голосов
/ 07 июля 2010

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

Вместо того, чтобы пытаться обнаруживать сгенерированные конструкторы, я просто изменил бы программу, чтобы разрешить отсутствующую документацию по любому конструктору по умолчанию. Большинство программ генерации документации, таких как NDoc и SandcastleGUI, имеют возможность добавлять стандартную документацию ко всем конструкторам по умолчанию; так что на самом деле нет необходимости документировать их вообще. Если у вас есть явный конструктор по умолчанию в вашем коде, вы можете поставить три слеша (///) над конструктором - ничего больше - чтобы отключить предупреждение Visual Studio об отсутствии документации.

4 голосов
/ 10 сентября 2012

Если вы хотите немного покопаться в ИЛ, тогда вы можете получить большинство пути туда.

Сначала, если у вас есть ConstructorInfo экземпляр, который, как вы знаете, не имеет параметров, вы можете получить тело метода и байты для тела метода следующим образом (для этого мы начнем создавать метод расширения):

public static bool MightBeCSharpCompilerGenerated(
    this ConstructorInfo constructor)
{
    // Validate parmaeters.
    if (constructor == null) throw new ArgumentNullException("constructor");

    // If the method is static, throw an exception.
    if (constructor.IsStatic)
        throw new ArgumentException("The constructor parameter must be an " +
            "instance constructor.", "constructor");

    // Get the body.
    byte[] body = constructor.GetMethodBody().GetILAsByteArray();

Вы можетеотклонить любые тела методов не имеют семь байтов.

    // Feel free to put this in a constant.
    if (body.Length != 7) return false;

Причина будет очевидна в следующем коде.

В разделе I.8.9.6.6 ECMA-335 (Разделы I-VI инфраструктуры общего языка (CLI)) , в нем указано правило 21 CLS:

CLS Правило 21: Конструктор объекта должен вызывать некоторый конструктор экземпляра своего базового класса перед любымдоступ происходит к унаследованным данным экземпляра.(Это не относится к типам значений, которые не обязательно должны иметь конструкторы.)

Это означает, что перед выполнением что-либо остальное базовый конструктор должен называться.Мы можем проверить это в IL.IL для этого будет выглядеть следующим образом (я поставил значения байтов в скобках перед командой IL):

// Loads "this" on the stack, as the first argument on an instance
// method is always "this".
(0x02) ldarg.0

// No parameters are loaded, but metadata token will be explained.
(0x28) call <metadata token>

Теперь мы можем начать проверять байты на это:

    // Check the first two bytes, if they are not the loading of
    // the first argument and then a call, it's not
    // a call to a constructor.
    if (body[0] != 0x02 || body[1] != 0x28) return false;

Теперь идет токен метаданных.Инструкция call требует, чтобы дескриптор метода был передан в виде токена метаданных вместе с конструктором.Это четырехбайтовое значение, которое предоставляется через свойство MetadataToken в MemberInfo классе (из которого происходит ConstructorInfo).

Мы может проверить, чтобы убедиться, что токен метаданных действителен, но поскольку мы уже проверили длину байтового массива для тела метода (в семи байтах), и мы знаем, что осталось проверить только один байт(первые два кода операции + четырехбайтовый токен метаданных = шесть байтов), нам не нужно проверять, что это конструктор без параметров;если бы были параметры, были бы другие операционные коды для выталкивания параметров в стек, расширяя байтовый массив.

Наконец, если больше ничего не делается в конструкторе (указывая, чтокомпилятор сгенерировал конструктор, который ничего не делает, кроме вызова базы), после вызова токена метаданных выдается инструкция ret:

(0x2A) ret

, которую мы можем проверить следующим образом:

    return body[6] == 0x2a;
}

Необходимо отметить, почему метод называется MightBeCSharpCompilerGenerated, с акцентом на Might .

Допустим, у вас есть следующие классы:

public class Base { }
public class Derived : Base { public Derived() { } }

При компиляции без оптимизации (обычно режим DEBUG), компилятор C # вставит несколько nop кодов (предположительно для помощи отладчику) для класса Derived, что вызовет вызов MightBeCSharpCompilerGenerated для возврата false.

Однако, когда оптимизация включена (обычно режим RELEASE), компилятор C # будет выдавать семибайтовое тело метода без кодов операций nop, поэтому он будет выглядетьлайкDerived имеет конструктор, сгенерированный компилятором, хотя его и нет.

Именно поэтому метод называется Might вместо Is или Has;это означает, что может быть методом, на который вам нужно обратить внимание, но не могу сказать наверняка.Другими словами, вы никогда не получите ложный отрицательный результат, но вам все равно придется расследовать, если вы получите положительный результат.

1 голос
/ 07 июля 2010

Следующий код вернет информацию о любых беспараметрических конструкторах вашего типа:

var info = typeof(MyClass).GetConstructor(new Type[] {});

Я не знаю способа различения конструктора по умолчанию и явно заданного конструктора без параметров.

Возможный обходной путь для вашей проблемы CommentChecker заключается в явном создании конструктора без параметров, где он необходим, и соответствующем комментировании.

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