问题 c#如何获取CallerMember的类型名称


我上了这堂课

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())
         {
             ....
         }
     }
}

我需要CallerMember的Type名称,有类似[CallerMemberName]的东西来获取Caller的类所有者?


11482
2017-07-27 03:02


起源

信息 CompilerServices 在我看来,提供的东西太少,无法从调用方法中获取类型。但是你可以尝试实际使用的是文件 [CallerFilePath],然后转到由给出的行 [CallerLineNumber] 并从那里找出班级名称。然后在调用程序集上使用反射来获取 Type 从你得到的名字。或者..考虑到可怕的性能和安全问题。提供 Type 作为一个论点是最简单的选择。 - NucS
“提供Type作为参数是最简单的选择”就是这个我不想要的,如果我有一个有20个方法的类需要为每个方法提供类名,如果需要重构这将是一个头痛。 Tks我正在考虑这种方式,但真的不喜欢它。 - Juan Pablo Gomez
走向堆栈以确定调用者类型是相当昂贵的,为什么你需要这个?有没有其他方法可以获得您想要的相同结果? - Jay
@Jay在我的应用程序中有一些监督的东西,我需要检查一个类的一个方法被标记为数据库作为监督。 - Juan Pablo Gomez
所以你只想要某些类型调用这些方法?如果你对调用方法有任何控制,你应该从那里开始,而不是在调用之后分支逻辑。 - Jay


答案:


它并非万无一失,但.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);
  }
}

9
2017-08-04 18:01



我希望有[CallerMemberType]!由于StackTrace方法由于内联而不稳定以及它没有为UWP实现,因此这是迄今为止我发现的最好的方法。 - thehelix


在我看来,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在评论中所说的那样,它可能相当昂贵,但它的工作做得很好。

编辑:

我找到了几个比较性能的arcticle,与它相比确实非常昂贵 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)
                  .ToArray();
        MethodsList = new List<MethodInfo>(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
    }
}

使用这种方法来满足您的精确需求。


4
2017-07-27 03:32



请帮忙。但这表现如何呢?我对这件事情不太熟悉。 - Juan Pablo Gomez
走路时你需要非常小心。当优化处于启用状态时,可以内联方法导致错误的类型。 - mike z
@JuanPabloGomez查看我对代码所做的修改。它将改善性能。 - NucS
@NucS Tks,我要去尝试一下。 - Juan Pablo Gomez
MethodImplOptions.NoInlining 不会阻止调用者的内联,这也会影响您的堆栈。这个样本太简单了,但是 what 可以内联到 Main。在这里你很幸运,它们是相同的类型,但一般来说它们不是。 - mike z


来电成员

当然,获取调用者成员名称  在对象模型中不是“自然的”。 这就是C#工程师在编译器中引入CallerMemberName的原因。

真正的敌人是重复,基于堆栈的解决方法是低效的。

[CallerMemberName] 允许获取信息而不会重复,也不会产生不良影响。

来电者类型

但得到呼叫者成员 类型   自然和 容易取得 没有重复。

怎么做

添加“调用者”参数 fooMethod,不需要特殊属性。

    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

而这正是你将得到的。

其他不会炒的情况。

虽然这完全符合您的要求,但还有其他不同的情况,它不起作用。

  • 当调用者是静态方法时(没有“this”)。
  • 当一个人想要调用者方法本身的类型,而不是调用者本身的类型(可能是第一个的子类)。

那些 案件,a [CallerMemberType] 可能有意义。 然而,这些案例的解决方案主要是调用者源文件的本地解决方案,因此它们并不是一个大问题恕我直言。


3
2017-11-08 08:46



请问答案是什么版本的c#支持它? - Juan Pablo Gomez
@JuanPabloGomez如果只需要调用者类型而不是方法名称,那么你就不需要了 [CallerMemberName] 和.NET 1.0就足够了。实现只是使用 Object.GetType() 在常规方法参数上。这就是这个答案的重点:有时,简单的事情很简单,而且很多年都很简单。 :-) - Stéphane Gourichon
对不起我现在明白你的意思了。我一直在寻找如何获得CallerMemberTypeName。无需传递另一个参数。但这是一个很好的建议 - Juan Pablo Gomez