Hello, I would like to tell you about some rarely used, but very useful attributes from the world of .NET.
So, let's talk about:
InternalsVisibleToAttribute
Why do you need? This remarkable attribute is to some extent a distant relative of the friend keyword from C ++. It allows any trusted assembly to access the internal types and internal members of the types of this assembly. This is a rather old attribute, it was added back in .NET 2.0.
When can it come in handy? For example, you can use it to write unit tests for internal types. (By default, you create classes marked as internal sealed, right?)
')
Example: Suppose you have a Domain.dll assembly that contains the internal type DefaultTimeScalingStrategy. Moreover, you have an assembly with unit tests Domain.Tests.dll. Let you want to add unit tests for the DefaultTimeScalingStrategy type. Then you just need to mark the Domain.dll assembly with the following attribute:
[assembly: InternalsVisibleTo("Domain.Tests")]
Note: If the Domain.Tests.dll assembly has a strong name, then you will have to use not only the assembly name, but also its public key (NOT a token).
TypeForwardedToAttribute
Why do you need? Indicates that a type has been moved from one assembly to another, and allows current users of the type to
not bother using it without recompiling clients. Generally speaking, this attribute is part of the implementation of type redirection in the CLR, which you can read about for example
here .
When can it come in handy? Suppose you are developing an assembly (Crypto.dll v1.0.0.0) that is in demand. I guess sometimes you rethink your decisions made in the past. For example, you have decided to move a type (BlumBlumShub) into a separate assembly (PRNG.dll).
You have two options:
- Just release a new version of the assembly (Crypto.dll v1.1.0.0), which now refers to the new assembly (PRNG.dll). But then your users will have to recompile the part of their applications that uses this type (BlumBlumShub), since it has been moved and the CLR has no idea where to look for it now.
- Release the new version of the assembly (Crypto.dll v1.1.0.0), which now refers to the new assembly (PRNG.dll), and give the CLR hint using the TypeForwardedToAttribute attribute on the new location of the type.
Example: Suppose you have an assembly (Crypto.dll) from which you want to transfer a type (BlumBlumShub) to another assembly (PRNG.dll). Then you just need to mark the Crypto assembly as follows:
[assembly: TypeForwardedTo(typeof(BlumBlumShub))]
and, in fact, transfer type.
Remarks:- Our hero has an antagonist - TypeForwardedFromAttribute .
- A real example of the use of sabzh is .NET 4.0 itself: the type <Action> fell its victim
- Interestingly, TypeForwardedFromAttribute is not a custom attribute (custom attribute), it is a pseudo custom attribute. Read more about pseudo attributes here .
- A more detailed example of the work of the subject can be found here .
CallerMemberNameAttribute
Why do you need? Allows you to get the name of the method that caused your code.
When can it come in handy? The attribute can be useful in two cases:
- Tracing and debugging code
- Implementing the INotifyPropertyChanged Interface
Example: With the help of the following simple code, you can save yourself from the magic lines when implementing the
INotifyPropertyChanged interface, which spoil the blood considerably when refactoring.
As it was before:
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)); } } }
How it became now:
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; }
Notice the definition and call of the
OnPropertyChanged method.
Remarks:- The subject has one drawback: it appeared only in .NET 4.5. It seems to be used in .NET 4.0 (when installing a specific patch), but certainly not in earlier versions.
- This attribute also has two brothers: CallerFilePathAttribute and CallerLineNumberAttribute . I hope their names speak for themselves.
MethodImplAttribute
Why do you need? This attribute defines the features of the implementation of a method. It can do a lot of things: from telling the compiler that the code of the method should not be optimized, until the execution of the code of the method in only one thread. All these facilities can be managed by selecting the correct bit flags from the
MethodImplOptions enumeration:
- Aggressiveinlining
- ForwardRef
- InternalCall
- NoInlining
- NoOptimization
- Preservesig
- Synchronized
- Unmanaged
When should it not be used? To be honest, I used only one flag from this enum, namely
Synchronized . But I would like to warn you against using it. Clever uncle J. Richter described an example in one of his remarkable
books , when the use of this flag can lead to mutual blocking of threads.
In short, for the
Synchronized flag and, for example, the following method:
public void Foo() {
the compiler generates something like this:
public void Foo() { lock(this) {
This can be fraught if another thread tries to impose a lock on the same
this object. Therefore, in order to avoid excesses, put a lock on the private field of the reference type instead of using
Synchronized , and everything will be fine with you.
Example: 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)); } }
Note: The
MethodImplAttribute attribute as well as
TypeForwardedToAttribute is a pseudo attribute. Moreover, to find out whether it was applied to a method or not, there is a special API, namely the
GetMethodImplementationFlags method of the
MethodInfo class.
That's all I wanted to tell.
If you have any questions, I will be happy to answer them according to your own strength and knowledge.