I have two classes like this:
public class test1: BaseClass
{
public test1() : base()
{
}
...
public class BaseClass
{
public BaseClass(
[CallerMemberName]string membername ="",
[CallerFilePath] string path = "")
{
var sf = new System.Diagnostics.StackTrace(1).GetFrame(0);
}
If I specify test1
ctor with call to base - I get membername
and path
initialized properly, but if not - compiler generates default constructor call, and membername
and path
are both empty.
Is this a bug or a feature ?
(Visual Studio 2019 16.11.8, net core 3.1 or net 5.0).
I've googled "c# compiler github" and ended up on https://github.com/dotnet/roslyn
After searching by CallerMemberName
- I've managed to find answer for this question:
https://github.com/dotnet/roslyn/issues/53757
It mentions that this is done by design.
But quickly scanning through the tickets lead me of thinking - "can I use attributes for same purpose ?"
Since what I was doing was unit testing - I've recoded my own attribute for that purpose: TestAttribute
=> FactAttribute
and resolved from NUnit method info that attribute, and got back
file path and method name.
public class FactAttribute : TestAttribute
{
public string FunctionName { get; }
public string FilePath { get; }
public FactAttribute( [CallerMemberName] string functionName = "", [CallerFilePath] string filePath = "")
{
FunctionName = functionName;
FilePath = filePath;
}
}
[TestFixture]
public class BaseClass
{
/// <summary>
/// Accesses private class type via reflection.
/// </summary>
/// <param name="_o">input object</param>
/// <param name="propertyPath">List of properties in one string, comma separated.</param>
/// <returns>output object</returns>
object getPrivate(object _o, string propertyPath)
{
object o = _o;
var flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
foreach (var name in propertyPath.Split('.'))
{
System.Type type = o.GetType();
if (char.IsUpper(name[0]))
o = type.GetProperty(name, flags).GetValue(o);
else
o = type.GetField(name, flags).GetValue(o);
}
return o;
}
[SetUp]
public void EachSpecSetup()
{
var mi = (MemberInfo)getPrivate(TestContext.CurrentContext.Test, "_test.Method.MethodInfo");
FactAttribute attr = mi.GetCustomAttribute<FactAttribute>();
string path = attr.FilePath;
string funcName = attr.FunctionName;
}
This allows to determine from which file and from which method call was directed from.