【问题标题】:How to get Type name of a CallerMember如何获取 CallerMember 的类型名称
【发布时间】:2013-07-27 11:02:06
【问题描述】:

我上了这门课

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 的类型名称,有类似 [CallerMemberName] 的东西来获取 Caller 的类所有者的类型吗?

【问题讨论】:

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

标签: c#


【解决方案1】:

这不是万无一失的,但.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);
  }
}

【讨论】:

  • 我希望有一个 [CallerMemberType]!由于 StackTrace 方法由于内联和未针对 UWP 实现的事实而不稳定,因此这是迄今为止我发现的最好的方法。
  • CallerMemberType 现在不可用
  • “但 .NET 的约定是每个文件有一种类型”——这对我来说是个新闻。我知道 Java 强制执行这个愚蠢的要求,但是 Microsoft 自己的最佳实践示例充满了 .cs 文件,在同一个文件中定义了多种类型。
  • @Dai 不是为了简洁/代码示例吗?例如,当涉及到企业应用程序时,我认为这不是您想要做的事情
【解决方案2】:

来电成员

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

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

[CallerMemberName] 允许在不重复和无不良影响的情况下获取信息。

调用者类型

但是获取调用者成员type 是自然的,容易获取没有重复。

怎么做

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# 开发团队列表中有更高优先级的项目我并不感到惊讶。

【讨论】:

  • 感谢答案,但什么版本的 c# 支持它?
  • @JuanPabloGomez 如果您只需要调用者类型而不是方法名称,那么您不需要 [CallerMemberName] 并且 .NET 1.0 就足够了。该实现只是在常规方法参数上使用Object.GetType()。这就是这个答案的全部要点:有时,简单的事情很简单,而且多年来一直很简单。 :-)
  • 对不起,我现在明白你的意思了。我正在寻找如何获取 CallerMemberTypeName。无需传递另一个参数。不过是个好建议
  • fooMethod(this);静态方法 ?
  • @Kiquenet 已经有一段时间了。你能举个具体的例子吗?为您提供提示可能会有所帮助。
【解决方案3】:

请参阅编辑 2 以获得更好的解决方案。

CompilerServices 提供的信息在我看来太少,无法从调用方法中获取类型。 您可以做的是使用StackTrace (see) 找到调用方法(使用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 在 cmets 中所说,它可能非常昂贵,但效果很好。

编辑:

我发现有几篇文章比较了性能,与同样被认为不是最好的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
    }
}

尝试使用这种方法来满足您的确切需求。

【讨论】:

  • 你在走栈时需要非常小心。当优化导致错误类型时,可以内联方法。
  • MethodImplOptions.NoInlining 不会阻止调用者的内联,这也会影响您的堆栈。这个示例太简单了,但what 可以内联到Main。在这里你很幸运,它们是同一类型,但总的来说它们不是。
  • @mikez 你有其他方法吗?我真的在这一点上停滞不前。谢谢你的帮助。
  • @mikez 我真的很怀疑。即使一个方法被内联到另一个方法中,当它被Reflection 访问时,它仍然会保留原来的Type 的内容,但重要的是要确保GetCallersType() 方法不会被内联。跨度>
  • @JuanPabloGomez 不,我没有。我只是指出这种方法的局限性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
  • 2012-03-24
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多