Reputation: 42266
Let's say I have an application with two files.
Console.cs
and Business.cs
Console.cs
has program Main
class.
Business.cs
has three classes named Customer
, Order
and Orderline
.
Is there anyway in C# to determine at runtime (maybe with reflection) that the business objects are in a file named Business.cs?
Upvotes: 3
Views: 3312
Reputation: 1045
This is now available via [CallerFilePathAttribute]
. It allows you to write a transparent debugging function like:
/// <summary>
/// Log the given expression to the console, returning that same value
/// transparently. Useful for debugging values without rewriting all
/// your code. Also logs the caller and line number via compiler
/// trickery.
/// </summary>
public T Dbg<T>(
T thingToLog,
// Ask the compiler to insert the current line number and caller
[CallerLineNumber] int lineNumber = 0,
[CallerMemberName] string caller = null,
[CallerFilePathAttribute] string filepath = null
)
{
if (System.Diagnostics.Debugger.IsAttached)
{
string filename = filepath.Split("\\").Last();
string stringToLog = typeof(T).IsArray ? "[ " + String.Join(", ", thingToLog) + " ]" : thingToLog.ToString();
Console.WriteLine($"[{filename}:{lineNumber} {caller}()] {stringToLog}");
}
return thingToLog;
}
Which will output messages like:
[MyFile.cs:228 FunctionThatCalledDbg()] value
Upvotes: 0
Reputation: 66
Assuming :
It means that, by adding this class in "NameSpaceGloballyVisibleByAllProjects":
using System;
using System.Runtime.CompilerServices;
namespace NameSpaceGloballyVisibleByAllProjects
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
public class MemorizeFilePathAttribute : Attribute
{
public string Path() { return _filepath; }
public MemorizeFilePathAttribute([CallerFilePath] string filepath = "")
{
_filepath = filepath;
}
readonly string _filepath;
}
}
You can simply use it like this:
using System.Reflection;
using NameSpaceGloballyVisibleByAllProjects;
Type type = typeof(Program);
var files = type.GetCustomAttributes<MemorizeFilePathAttribute>(false).Select(att => att.Path).ToList();
Note: As you notice there are more than one file! This is because of "partial" keyword in C#. So it's up to you to use "files.Single()" or not...
We just need to add this attribute above all types now We can do that in Visual Studio with Ctr-H (Find-and-Replace).
Upvotes: 0
Reputation: 4421
not sure what your use case is, however if some one is calling you then you can add compiler directives
[CallerFilePath] string file = "", [CallerLineNumber] int LineNo = 0
in your method.
if not than your best way of accessing this is by using the .pdb file that get's generated. The format is published and a C++ dll is available that can be used to access the file however the easiest way to read the file (and possible line number) if included in the pdb file is using stacktrace
You can access the stack in an exception, so if a class allows you to throw an exception by passing null where you should not than try catch it and you have your stack trace.
if you need the calling file but do not want to add the compiler directives as some one can simply overwrite it you can do something like:
StackTrace st = new StackTrace(new StackFrame(1));
st.GetFrame(1).GetFileName());
Upvotes: 0
Reputation: 74822
The C# compiler does not emit this information into the DLL, so it's not available through reflection. However, as you'll be aware from debugging, the debugger can match up compiled locations to source code locations. It does this through PDB files. So it might be theoertically possible for you to ship your PDB files, and invoke the unmanaged debugger or diagnostic symbol store API (see General Reference > Unmanaged API Reference in MSDN) to determine where a given method was defined. You can't really do this for a class, though, because a class could be spread across multiple files using partial classes.
Upvotes: 1
Reputation: 4019
If you compile in debug mode you can probably use Cecil (part of Mono project) to extract the source filenames from the debug symbols. But when you compile in release mode this information probably gets lost.
However, if you need to do this, for other purposes than for example static analysis of your software, you are probably on the wrong track and should think of another solution.
If you put the classes in a Business namespace you could use reflection to find if an object comes from that namespace:
namespace Business {
class Customer {}
class Order {}
class OrderLine {}
}
var myObject = new Customer();
Console.WriteLine(myObject.GetType().Namespace); // writes "Business"
Upvotes: 1
Reputation: 17274
*.PDB (debug info files) files should have that information. Otherwise I see no way to get it, since code files is just an abstraction which compiled code should not care about.
Upvotes: 0
Reputation: 30700
I believe the closest you'll get is typeof(Customer).Assembly.Location
. However, this will only give you the DLL, not the location of the source code (which makes sense, since the source code would normally not be included with the binaries).
Upvotes: 0