Пусть есть класс
class Class
{
}
и атрибут
class SomePropertyAttribute : Attribute
{
public SomePropertyAttribute(string prop)
{
}
}
Почему компилятор не выдаёт ошибку, если применить атрибут к классу следующим образом
[SomeProperty(Weird)] //<-- Weird const use
class Class
{
private const string Weird = nameof(Weird);
}
?
Ведь в данном случае приватная константа используется за пределами фигурных скобок, определяющих класс. Тогда как документация (см. private) гласит:
Ключевое слово private является модификатором доступа к члену. ... Доступ к закрытым членам можно получить только внутри тела класса или структуры, в которой они объявлены ...
Как ни странно, ваш пример не противоречит спецификации языка.
Я не смог сам найти и правильно проинтерпретировать нужную цитату из стандарта, но мне помог аналогичный вопрос в гитхаб-репозитории компилятора. Как оказалось, разрешение имён (name resolution) для атрибутов класса проходит в контексте самого класса. Поэтому, кстати, вы можете ссылаться на константу просто как Weird
, а не Class.Weird
. «Нарушения» правил видимости тут не происходит, атрибут как бы находится внутри класса. Давайте найдём подтверждение этого в спецификации.
На этапе разрешения имён, Weird
классифицируется как «простое имя» (simple name), и его привязка регулируется разделом 7.6.2 спецификации. Релевантная часть:
K
is zero and the simple_name appears within a block ... [не подходит — VladD]K
is zero and the simple_name appears within the body of a generic method declaration ... [не подходит — VladD]T
(The instance type), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):Лирическо-бюрократическое отступление.
Подходит ли нам эта часть? Лежит ли атрибут вообще в какой либо декларации типа (immediately enclosing type declaration)? Кажется, что он лежит снаружи, но это не так. Согласно разделу 10.1, декларация класса является декларацией типа
A class_declaration is a type_declaration
и включает в себя объявление атрибутов:
A class_declaration consists of an optional set of attributes (Attributes), followed by...
Возвращаемся к теме. У нас T
есть тип Class
. В спецификации есть следующие подпункты:
K
is zero and the declaration of T
includes a type parameter with name I
... [не подходит, у нас нет параметров-типов — VladD]I
in T
with K
type arguments produces a matchЭта часть подходит, т. к. Weird
является константой-членом класса (в чём можно убедиться, пролистав раздел Member lookup).
Итак, в этом случае Weird
разрешается как часть типа Class
.
Атрибуты в C# добавляют метаданные на сборку. Если точнее - и на наш класс.
В этой ситуации область видимости атрибута и константы совпадают на уровне компиляции. Поэтому ваш пример работает.
TypeDef #2 (02000003)
-------------------------------------------------------
TypDefName: XmlParsing.Class (02000003)
Flags : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100000)
Extends : 01000011 [TypeRef] System.Object
Field #1 (04000001)
-------------------------------------------------------
Field Name: Weird (04000001)
Flags : [Private] [Static] [Literal] [HasDefault] (00008051)
DefltValue: (String) We
CallCnvntn: [FIELD]
Field type: String
Signature : 06 0e
Method #1 (06000002)
-------------------------------------------------------
MethodName: .ctor (06000002)
Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886)
RVA : 0x0000205a
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
Signature : 20 00 01
CustomAttribute #1 (0c00000f)
-------------------------------------------------------
CustomAttribute Type: 06000001
CustomAttributeName: XmlParsing.SomePropertyAttribute :: instance void .ctor(class System.String)
Length: 10
Value : 01 00 05 57 65 69 72 64 00 00 > Weird <
ctor args: ("Weird")
Виртуальный выделенный сервер (VDS) становится отличным выбором
Возможно ли перегрузить метод ConsoleWriteLine ?