如何获取呼叫者成员的类型名称
本文关键字:成员 类型 呼叫者 何获取 获取 | 更新日期: 2023-09-27 18:25:19
我得到了这个类
public class fooBase
{
public List<MethodsWithCustAttribute> MethodsList;
public bool fooMethod([CallerMemberName]string membername =""))
{
//This returns a value depending of type and method
}
public void GetMethods()
{
// Here populate MethodsList using reflection
}
}
和这个属性类
// This attribute get from a database some things, then fooMethod check this attribute members
public class CustomAttribute
{
public string fullMethodPath;
public bool someThing ;
public bool CustomAttribute([CallerMemberName]string membername ="")
{
fullMethodPath = **DerivedType** + membername
// I need here to get the type of membername parent.
// Here I want to get CustClass, not fooBase
}
}
然后我有这个
public class CustClass : fooBase
{
[CustomAttribute()]
public string method1()
{
if (fooMethod())
{
....
}
}
}
我需要调用者成员的类型名称,有类似 [调用者成员名称] 的东西来获取调用者的类所有者的类型?
它不是万无一失的,但 .NET 的约定是每个文件有一个类型,并将文件命名为与类型相同的名称。我们的工具也倾向于强制执行这个约定,即Resharper和Visual Studio。
因此,从文件路径推断类型名称应该是合理的。
public class MyClass
{
public void MyMethod([CallerFilePath]string callerFilePath = null, [CallerMemberName]string callerMemberName = null)
{
var callerTypeName = Path.GetFileNameWithoutExtension(callerFilePath);
Console.WriteLine(callerTypeName);
Console.WriteLine(callerMemberName);
}
}
调用方成员
当然,获取调用方成员名称在对象模型中并不"自然"。这就是 C# 工程师在编译器中引入 CallerMemberName 的原因。
真正的敌人是重复,而基于堆栈的解决方法效率低下。
[CallerMemberName]
允许在没有重复和不良影响的情况下获取信息。
呼叫者类型
但是获取调用方成员类型是很自然的,并且很容易获得,而不会重复。
怎么做
在fooMethod
中添加"caller"参数,不需要特殊属性。
public bool fooMethod(object caller, [CallerMemberName]string membername = "")
{
Type callerType = caller.GetType();
//This returns a value depending of type and method
return true;
}
并像这样称呼它:
fooMethod(this);
这回答了问题
你说
在这里我想得到CustClass,而不是fooBase
而这正是你会得到的。
其他不起作用的情况,并提供解决方案。
虽然这完全可以满足您的要求,但在其他不同的情况下,它不起作用。
- 情况 1:当调用者是静态方法时(没有"this"(。
- 情况 2:当需要调用者方法本身的类型,而不是调用者本身的类型(可能是第一个的子类(时。
在这些情况下,[CallerMemberType]
可能是有意义的,但有更简单的解决方案。请注意,静态调用者的情况更简单:没有对象,因此它与调用方法的类型之间没有差异。没有fooBase
,只有CustClass
。
情况 1:当调用方是静态方法时(没有"this"(
如果至少有一个调用方是静态方法,则不要在方法内部执行GetType()
,而是在调用站点上执行,因此不要将"this"传递给方法,而是将类型传递给:
public bool fooMethodForStaticCaller(Type callerType, [CallerMemberName]string membername = "")
静态调用方将执行以下操作:
public class MyClassWithAStaticMethod // can be CustClass, too
{
public static string method1static()
{
fooMethodForStaticCaller(typeof(MyClassWithAStaticMethod));
}
}
为了保持与对象调用方的兼容性,请保留采用this
指针的其他fooMethod
,或者您可以删除它,对象调用方将执行以下操作:
fooMethod(this.GetType());
您可以注意到上面的typeof(MyClassWithAStaticMethod)
重复了类名,这是真的。 最好不要重复类名,但这没什么大不了的,因为这只重复一次,作为类型化项目(不是字符串(并且在同一类中。 这并不像[CallerMemberName]
解决的原始问题那么严重,即在所有呼叫站点中重复呼叫者姓名的问题。
情况 2:当需要调用者方法的类型而不是调用者的类型
例如,在类fooBase
中,您希望从对象上下文中调用anotherFooMethod
,但希望传递的类型始终fooBase
,而不是对象的实际类型(例如 CustClass
(。
在这种情况下,有一个this
指针,但您不想使用它。 因此,只需使用实际相同的解决方案:
public class fooBase
{
[CustomAttribute()]
public string method1()
{
if (anotherFooMethod(typeof(fooBase)))
{
....
}
}
}
就像在案例 1 中一样,有一个重复,而不是每个调用站点一个,除非您有一个预先存在的猖獗的代码重复问题,在这种情况下,这里要解决的问题不是你应该担心的问题。
结论
[CallerMemberType]
避免重复可能仍然有意义,但是:
- 添加到
- 编译器的任何内容都是维护成本的复杂性负担
- 鉴于现有的解决方案,我并不感到惊讶,C#开发团队列表中有优先级更高的项目。
有关更好的解决方案,请参阅编辑 2。
在我看来,CompilerServices
提供的信息太少,无法从调用方法中获取类型。你可以做的是使用 StackTrace
(参见( 查找调用方法(使用 GetMethod()
(,并从那里使用 Reflection
获取类型。
请考虑以下事项:
using System.Runtime.CompilerServices;
public class Foo {
public void Main() {
what();
}
public void what() {
Bar.GetCallersType();
}
public static class Bar {
[MethodImpl(MethodImplOptions.NoInlining)] //This will prevent inlining by the complier.
public static void GetCallersType() {
StackTrace stackTrace = new StackTrace(1, false); //Captures 1 frame, false for not collecting information about the file
var type = stackTrace.GetFrame(1).GetMethod().DeclaringType;
//this will provide you typeof(Foo);
}
}
}
注意 - 正如@Jay在评论中所说,它可能非常昂贵,但它做得很好。
编辑:
我发现有几个北极比较性能,与Reflection
相比,它确实非常昂贵,这也被认为是最好的。
参见: [1] [2]
编辑 2:
所以在深入了解StackTrace
之后,使用它确实不安全,甚至昂贵。
由于每个将被调用的方法都将用[CustomAttribute()]
标记,因此可以将包含它的所有方法收集到静态列表中。
public class CustomAttribute : Attribute {
public static List<MethodInfo> MethodsList = new List<MethodInfo>();
static CustomAttribute() {
var methods = Assembly.GetExecutingAssembly() //Use .GetCallingAssembly() if this method is in a library, or even both
.GetTypes()
.SelectMany(t => t.GetMethods())
.Where(m => m.GetCustomAttributes(typeof(CustomAttribute), false).Length > 0)
.ToList();
MethodsList = methods;
}
public string fullMethodPath;
public bool someThing;
public CustomAttribute([CallerMemberName] string membername = "") {
var method = MethodsList.FirstOrDefault(m=>m.Name == membername);
if (method == null || method.DeclaringType == null) return; //Not suppose to happen, but safety comes first
fullMethodPath = method.DeclaringType.Name + membername; //Work it around any way you want it
// I need here to get the type of membername parent.
// Here I want to get CustClass, not fooBase
}
}
尝试这种方法以满足您的确切需求。
为什么不直接使用public void MyMethod<T>(params) { string myName = typeof(T).Name }
那就叫它Logger.MyMethod<Form1>(...);
如果您只需要基本信息,则可以避免反射对性能的影响。