If you are building a class library with Retargetable turned on, you might end up your assemblies to be used by some environment that does not support some of the functionality that is well required by the assembly. Let us consider the case with Caller Info Attributes.
Caller Info attributes are a good way of determining the name of the method, the line number and the file path of the source file for which the line is actually getting executed. It has been introduced with .NET 4.5 but was not a part of the other frameworks yet released. It is worth noting the fact, the previous version of the environment still have these info embedded within the runtime while running the code at least in debug mode, yet these functionalities were never been exposed.
The new compiler allows you to exploit this runtime information and use it inside your code. For instance, lets say you are adding a logger component to an assembly :
public static void Log([CallerMemberName]string memberName = "", [CallerLineNumber] int ln = 0, [CallerFilePath]string path ="") { Logger.Write("{0} Line : {1} on file {2}", memberName, ln, path); }
Now the above line will log the line to a logger considering the fact that you have implemented the Logger utility somewhere which writes the log either to the Text file or some medium which suits you. But with the code, you are relying completely to the compiler to determine the values you set for the variables.
Now the trick that the compiler does is to replace the values of the variables with the actual line, member name and the path during compilation. Thus if you see the compiled code you would eventually see the actual values rather than the variables.
This transformation is done by the compiler. In the above image, you can see the call to MyType.Log, is replaced with the actual values even when you don’t send any argument to the method. The Compiler tries to find the attributes for the transformation and replaces every call with the appropriate values.
Trick to add the Caller info where it doesn’t support :
1. Add the Caller Attributes yourself. As the transformation is done by the compiler, and not the runtime, you can simply define the Attribute yourself inside the assembly to support this transformation.
namespace System.Runtime.CompilerServices { // Summary: // Allows you to obtain the method or property name of the caller to the method. [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] sealed class CallerMemberNameAttribute : Attribute { } // Summary: // Allows you to obtain the line number in the source file at which the method // is called. [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] public sealed class CallerLineNumberAttribute : Attribute { } // Summary: // Allows you to obtain the full path of the source file that contains the caller. // This is the file path at the time of compile. [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] public sealed class CallerFilePathAttribute : Attribute { } }
When you place this code into an assembly where it isnt supported and you compile the code (for instance the Portable Class Library), the compiler replaces the code with appropriate data itself.
2. Add Retargetable flag to the assembly, so that the assembly could be used in multiple environments.
3. Compile the assembly and put it in GAC (Global Assembly Cache).
You are done !!
You can use this trick to add Caller Info attributes to any environment where it isnt supported, even in Portable environments.