32

我上了这门课

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 的类所有者的类型吗?

4

3 回答 3

24

这不是万无一失的,但 .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);
  }
}
于 2017-08-04T18:01:31.897 回答
14

来电成员

当然,在对象模型中获取调用者成员名称不是“自然的”。这就是 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

这正是你会得到的。


其他不起作用的情况,有解决方案。

虽然这完全满足了您的要求,但在其他不同的情况下它不起作用。

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

这些情况下,a[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));
    }
}

为了保持与对象调用者的兼容性,要么保留另一个fooMethod获取this指针的对象,要么您可以删除它,对象调用者将执行以下操作:

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# 开发团队列表中有更高优先级的项目。
于 2017-11-08T08:46:11.913 回答
6

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

在我看来,提供的信息CompilerServices太少,无法从调用方法中获取类型。您可以做的是使用StackTrace( see ) 找到调用方法 (using GetMethod()) 并Reflection从那里获取 using 的类型。
考虑以下:

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
    }
}

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

于 2013-07-27T03:32:36.107 回答