The currently supported caller info attributes can only provide the name of the method, the name of the source file and the line number of the call in the source file. This is fine for scenarios such as simplifying implementations of INotifyPropertyChanged
. However, if using these attributed for logging the amount of information available is quite limited.
Expand the number of supported caller info attributes to allow embedding additional diagnostic information. The following list is quite expansive to discuss/argue over the potential possibilities.
CallerColumnNumberAttribute: The column number of where the method is invoked.
CallerTypeNameAttribute: The simple name of the declaring type of the calling method.
CallerNamespaceAttribute: The namespace of the declaring type of the calling method.
CallerFullTypeNameAttribute: The full name of the declaring type of the calling method.
CallerTypeAttribute: The declaring type of the calling method. This is replaced by the compiler by ldtoken
of the type followed by a call to Type.GetTypeFromHandle
.
CallerMethodAttribute: The MethodBase of the calling method. This is replaced by the compiler by ldtoken
of the method reference followed by a call to MethodBase.GetMethodFromHandle
.
Example usage:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Project1 {
public static class Program {
private static void Foo([CallerMemberName] string memberName = null,
[CallerTypeName] string typeName = null,
[CallerNamespaceName] string namespaceName = null,
[CallerFullTypeName] string fullTypeName = null,
[CallerFilePath] string filePath = null,
[CallerLineNumber] int lineNumber = 0,
[CallerColumnNumber] int columnNumber = 0,
[CallerType] Type type,
[CallerMethod] MethodBase method)
{
Debug.Assert(memberName == "Main");
Debug.Assert(typeName == "Program");
Debug.Assert(namespaceName == "Project1");
Debug.Assert(fullTypeName == "Project1.Program");
Debug.Assert(filePath == "c:\\foo\\bar\\Program.cs");
Debug.Assert(lineNumber == 29);
Debug.Assert(columnNumber == 12);
Debug.Assert(type == typeof(Program));
Debug.Assert(method == typeof(Program).GetMethod("Main"));
}
public static void Main() {
Foo();
}
}
}
That's a lot of optional parameters, maybe time for an object?
Public Foo( [CallerInfo] As CallerInfo = null);
CallerInfo
Class CallerInfo
Public ReadOnly MemberName As String
Public ReadOnly TypeName As String
Public ReadOnly Namespace As String
Public ReadOnly FullTypeName As String
Public ReadOnly FilePath As String
Public ReadOnly LineNumber As Integer
Public ReadOnly ColumnNumber As Integer
Public ReadOnly Type As Type
Public ReadOnly Method As MethodBase
End Class
Maybe. I wouldn't imagine that they would all be used in conjunction, but that could be useful as a convenience.
@HaloFour Some attributes seem redundant:
| Attribute | Equvalent |
| --- | --- |
| [CallerTypeName] | CallerMethod.ReflectedType.Name |
| [CallerNamespace] | CallerMethod.ReflectedType.Namespace |
| [CallerFullTypeName] | CallerMethod.ReflectedType.FullName |
| [CallerType] | CallerMethod.ReflectedType |
@ashmind
That they are. Technically you wouldn't need anything beyond [CallerMethod]
, [CallerFilePath]
, [CallerLineNumber]
and [CallerColumnNumber]
in order to derive all of that information, and if those were the only attributes that existed then I would be completely happy. It is the existing attribute [CallerMemberName]
which set the precedent for attributes only resulting in portions of the metadata and so I simply completed the story.
This would be very useful in log libraries, like NLog. We now use stacktrace for that, but those are not implemented in the new framework (yet?) https://github.com/dotnet/corefx/issues/1797
I think one object/struct would be better then a those optional parameters.
If this will be implemented with a new type (e.g. Foo([CallerInfo] ICallerInfo)
), it would be also nice if that call has preference over Foo()
. AFAIK this is now not the case, so a change in the caller attributes is a breaking change (not binary backwards-compatible).
I suppose CallerTypeName & CallerType etc. should be named CallerOwnerTypeName & CallerOwnerType and so on...
And CallerType etc. or additional CallerMemberType etc. should be of the method return type (in case of property accessor, it has to be the property type).
This has been moved to https://github.com/dotnet/csharplang/issues/87
Most helpful comment
That's a lot of optional parameters, maybe time for an object?
CallerInfo