- PVSM.RU - https://www.pvsm.ru -
Здравствуйте, я хотел бы вам рассказать о некоторых редко используемых, но весьма полезных атрибутах из мира .NET.
Итак, поговорим о:
Зачем нужен? Этот замечательный атрибут является в какой-то мере дальним родственником ключевому слову friend из C++. Он позволяет любой доверенной сборке обращаться к internal типам и internal членам типов данной сборки. Это довольно старый атрибут, он был добавлен ещё в .NET 2.0.
Когда может пригодиться? Например, его можно использовать для написания модульных тестов для internal типов.(Вы ведь по умолчанию создаете классы помеченные как internal sealed, правда?)
Пример: Предположим, у вас есть сборка Domain.dll, которая содержит internal тип DefaultTimeScalingStrategy. Более того, у вас есть сборка с модульными тестами Domain.Tests.dll. Пусть вы хотите добавить модульные тесты для типа DefaultTimeScalingStrategy. Тогда вам достаточно пометить сборку Domain.dll следующим атрибутом:
[assembly: InternalsVisibleTo("Domain.Tests")]
Замечание: Если сборка Domain.Tests.dll имеет строгое имя, тогда придется использовать не только имя сборки, но и её публичный ключ(НЕ токен).
Зачем нужен? Указывает, что тип был перенесен из одной сборки в другую, и позволяет текущим потребителям типа не заморачиваться использовать его без перекомпиляции клиентов. Говоря в общем, этот атрибут является частью реализации переадресации типов в CLR, о которой можно почитать например здесь [5].
Когда может пригодиться? Предположим, вы разрабатываете сборку (Crypto.dll v1.0.0.0), которая пользуется спросом. Полагаю, иногда вы переосмысливаете свои решения принятые в прошлом. Например, вы задумали вынести какой-либо тип(BlumBlumShub) в отдельную сборку(PRNG.dll).
У вас есть два пути:
Пример: Пусть у вас есть сборка(Crypto.dll), из которой вы хотите вынести тип(BlumBlumShub) в другую сборку(PRNG.dll). Тогда вам будет достаточно пометить сборку Crypto следующим образом:
[assembly: TypeForwardedTo(typeof(BlumBlumShub))]
и, собственно, перенести тип.
Замечания:
Зачем нужен? Позволяет получить имя метода, который вызвал ваш код.
Когда может пригодиться? Атрибут может быть полезен в двух случаях:
Пример: С помощью следующего нехитрого кода можно избавить себя от магических строк при реализации интерфейса INotifyPropertyChanged, которые изрядно портят кровь при рефакторинге.
Как было раньше:
internal sealed class Star : INotifyPropertyChanged
{
private int _luminosity;
public int Luminosity
{
get { return _luminosity; }
set
{
if (value != _luminosity)
{
_luminosity = value;
OnPropertyChanged("Luminosity");
}
}
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Как стало сейчас:
internal sealed class Star : INotifyPropertyChanged
{
private int _luminosity;
public int Luminosity
{
get { return _luminosity; }
set
{
if (value != _luminosity)
{
_luminosity = value;
OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Обратите внимание на определение и вызов метода OnPropertyChanged.
Замечания:
Зачем нужен? Этот атрибут определяет особенности реализации того или иного метода. Может он много чего: от указания компилятору того, что код метода не должен быть оптимизирован, до исполнения кода метода только в одном потоке. Всем этим хозяйством можно управлять выбирая правильные битовые флаги из перечисления MethodImplOptions [12]:
Когда его НЕЛЬЗЯ использовать? Признаться честно, я использовал только один флаг из этого перечисления, а именно Synchronized. Но я хотел бы вас предостеречь от его использования. Умный дядька Дж. Рихтер описал в одной своей замечательной книге [13]пример, когда использование этого флага может привести к взаимной блокировке потоков.
Если кратко, то для флага Synchronized и, например, следующего метода:
public void Foo()
{
// Do something
}
компилятор cгенерирует примерно вот такой код:
public void Foo()
{
lock(this)
{
// Do something
}
}
Это может быть чревато в том случае, когда другой поток попытается наложить блокировку на этот же объект this. Поэтому, во избежание эксцессов, накладывайте блокировку на приватное поле ссылочного типа вместо использования Synchronized, и всё у вас будет хорошо.
Пример:
public static class MathUtility
{
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
public static int GetFibonacciNumber(int n)
{
return (int)((Math.Pow((1 + Math.Sqrt(5))/2, n) - Math.Pow((1 - Math.Sqrt(5))/2, n))/Math.Sqrt(5));
}
}
Замечание: Атрибут MethodImplAttribute также как и TypeForwardedToAttribute является псевдо атрибутом. Более того, чтобы узнать был ли он применен к методу или нет, существует специальное API, a именно метод GetMethodImplementationFlags [14] класса MethodInfo.
Вот и всё, что я хотел рассказать.
Если у вас возникли какие-либо вопросы, я буду рад ответить на них по мере своих сил и знаний.
Автор: Saladin
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/net/47890
Ссылки в тексте:
[1] InternalsVisibleToAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute(v=vs.110).aspx
[2] TypeForwardedToAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.typeforwardedtoattribute(v=vs.110).aspx
[3] CallerMemberNameAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx
[4] MethodImplAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimplattribute(v=vs.110).aspx
[5] здесь: http://msdn.microsoft.com/en-us/library/ms404275(v=vs.110).aspx
[6] TypeForwardedFromAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.typeforwardedfromattribute(v=vs.110).aspx
[7] Action<,>: http://msdn.microsoft.com/en-us/library/bb549311(v=vs.100).aspx
[8] здесь: https://www.simple-talk.com/blogs/2010/11/30/subterranean-il-pseudo-custom-attributes
[9] здесь: http://sergeyteplyakov.blogspot.com/2013/10/net.html
[10] CallerFilePathAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callerfilepathattribute(v=vs.110).aspx
[11] CallerLineNumberAttribute: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callerlinenumberattribute(v=vs.110).aspx
[12] MethodImplOptions: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimploptions(v=vs.110).aspx
[13] книге : http://www.amazon.com/CLR-via-C-Developer-Reference/dp/0735667454
[14] GetMethodImplementationFlags: http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getmethodimplementationflags.aspx
[15] Источник: http://habrahabr.ru/post/201396/
Нажмите здесь для печати.