简体   繁体   English

C#类是否知道实例化了哪个文件?

[英]Does a C# class know what file instantiated it?

Is it possible to tell what file instantiated a class in C#? 是否可以告诉哪个文件在C#中实例化了一个类?

For example, If I had Page1.cs and Page2.cs could a constructor in myclass.cs know what page created an object from it? 例如,如果我有Page1.cs和Page2.cs,myclass.cs中的构造函数可以知道哪个页面从中创建了对象吗?

You can do this via the " Caller Information " attributes. 您可以通过“ 呼叫者信息 ”属性来执行此操作。 Essentially you create some extra optional parameters on your class' constructor, apply some special attributes to them, and the compiler will fill in the details for you automatically. 本质上,您是在类的构造函数上创建一些额外的可选参数,并向它们应用一些特殊属性,然后编译器会自动为您填充详细信息。 For example: 例如:

using System.Runtime.CompilerServices;

public MyClass
{
    public MyClass(  
    [CallerMemberName] string memberName = "",  
    [CallerFilePath] string sourceFilePath = "",  
    [CallerLineNumber] int sourceLineNumber = 0)  
    {
        ...
    }
}

You just need to call it as: 您只需要将其称为:

var instance = new MyClass();

and the compiler will fill in the caller's member name, file path and line number automatically. 编译器将自动填写调用者的成员名称,文件路径和行号。

A class can learn what class instantiated it by inspecting the stack trace during construction. 一个班级可以通过检查构造期间的堆栈跟踪来了解实例化了哪个班级。 So for example if you were to add this to your class' constructor: 因此,例如,如果要将其添加到类的构造函数中:

var creator = new StackTrace().GetFrame(1).GetMethod().DeclaringType.FullName;

...you'd learn the location of the code that called new . ...您将学到调用new的代码的位置。 Location as in the name of the class. 位置与班级名称相同。 You can of course inspect the declaring type's properties to learn the assembly name, location, etc. 您当然可以检查声明类型的属性以了解程序集名称,位置等。

Just bear in mind that you'd have to walk the stack frame a bit further if you have chained constructors. 请记住,如果您链​​接了构造函数,则必须将堆栈框架走得更远。 Also, this won't work for any object that was created through deserialization. 同样,这对于通过反序列化创建的任何对象均无效。

SOLUTION 1 (requires .NET 4.5 and editing your code ) 解决方案1(需要.NET 4.5并编辑您的代码

Suppose that this is Caller.cs 假设这是Caller.cs

public class Caller
{
    public Caller()
    {
        new Callee();
    }
}

and this is Callee (the class that will be called): 这就是Callee(将被调用的类):

using System.Runtime.CompilerServices;
...

public class Callee
{
    public Callee([CallerFilePath] string callerFileName = "")
    {
        Console.WriteLine(callerFileName);
    }
}

The output will be 输出将是

c:\users\francesco\source\repos\ConsoleApp19\ConsoleApp19\Caller.cs

A longer explanation is eg here ; 更长的解释在这里 ; [CallerFilePath] will take care of retrieving the caller file name and store it in the callerFileName parameter of the constructor of Callee . [CallerFilePath]将负责检索调用者文件名,并将其存储在Callee的构造函数的callerFileName参数中。 This requires editing your source code and at least .NET 4.5 , which I'm not sure is a requirement every application will satisfy. 这需要编辑您的源代码,至少需要编辑.NET 4.5 ,我不确定每个应用程序都可以满足。


SOLUTION 2 (requires just editing your code ) 解决方案2(只需要编辑代码

So, you can just change the constructor of Callee to pass it a string parameter, which will be the name of your Caller (eg "Caller.cs" ): 因此,您只需更改Callee的构造函数以向其传递一个字符串参数,该参数将成为您的Caller的名称(例如"Caller.cs" ):

 public class Caller { public Caller() { new Callee("Caller.cs"); } } public class Callee { public Callee(string callerFileName = "") { Console.WriteLine(callerFileName ); } } 

Which of course will be a viable solution if you just have a few classes ( Caller being one of them) and will work with every version of the .NET framework. 如果您只有几个类( Caller是其中的一个),那么哪种当然将是可行的解决方案,并且适用于每个.NET框架版本。

Anyway it's not recommended to use a file to host several classes, but it might have been done by someone else in legacy code: so you can get the file name but still not the calling class, which is why you can use the method I just listed (directly passing the name of the caller to the constructor) instead of the first. 无论如何,不​​建议使用文件来承载多个类,但是它可能是由遗留代码中的其他人完成的:因此您可以获得文件名,但仍不是调用类,这就是为什么您可以使用我刚才提到的方法的原因列出(将调用方的名称直接传递给构造函数),而不是第一个。


SOLUTION 3 (requires no code edits ) 解决方案3(无需进行代码编辑

Last but not least, if you are just debugging from Visual Studio, you don't have to do any of the above: just use StackFrame: set a breakpoint to the constructor of Callee and click on the StackFrame dropdown: 最后但并非最不重要的一点是,如果您只是从Visual Studio调试,则无需执行上述任何操作:只需使用StackFrame:为Callee的构造函数设置一个断点,然后单击StackFrame下拉列表:

StackFrame下拉菜单

This will require no code editing whatsoever, and clearly shows that the constructor of Callee is called by Caller ; 这将不需要任何代码编辑,并且清楚地表明Callee的构造函数是由Caller you can just click on any line of the menu and you will be brought to the calling line. 您只需单击菜单的任何一行,您就会被带到呼叫行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM