【问题标题】:How to determine method caller type at run time? [duplicate]如何在运行时确定方法调用者类型? [复制]
【发布时间】:2015-09-11 04:47:40
【问题描述】:

我想知道是否有一种方法可以在运行时确定调用它的类型。

[UPD2] 对于这种特殊情况,我只对调用者的类型感兴趣,而不是方法的名称,因此,How can I find the method that called the current method?Retrieving the calling method name from within a method 等其他类似问题并不能解决我的问题。

为了简单起见,假设我有一个名为AnimalContainer 的类。 在初始化时,它的构造函数会收到一个允许的类型列表:允许将动物注册到容器的类型。

这个类(类似于 IoC 容器)的目标是为希望检索特定动物实例的任何类型充当容器。限制是只有任意一组类型才能将动物注册到容器中。然后,如果一个不允许的类型调用该方法,该方法将返回一个InvalidOperationException

期望类型或对象参数:

void RegisterAnimal(Type callerType, Animal animal) {...}
// Or
void RegisterAnimal(object invoker, Animal animal) {...}

这些不起作用,因为调用者不限于指定自己的类型(或实例)。例如:

// Inside class A:
animalContainer.RegisterAnimal(typeof(B), anyAnimal);

[UPD1] 我已经看到很多答案表明这似乎是一个糟糕的方法。如果你这样考虑,你会建议另一种方式吗?一般来说,我想要实现的是防御性编码。 按照惯例,我当然可以拥有允许在该容器中注册动物的一组类型。会发生什么,是否有任何其他不了解这种约定的开发人员尝试在一个不应该这样做的类中注册动物?它会做一些设计没有预料到的事情,所以,很可能最终会引入一些错误。 这只是我能找到的更简单的例子(The AnimalContainer),但它可以扩展到大量的情况:

  1. 拥有一组控制器类,可以将依赖项注册到 IoC 容器中。
  2. 拥有一个 MessageContainer,为不同类型之间的消息注册发布者和订阅者(​​将事件订阅者应该知道谁在发布事件这一事实分离)。然后指定只有一组类型可以发布给定消息。 例如:任何类都可以监听 NewAnimalArrive 消息,但只有一组特定的类可以触发该事件。

【问题讨论】:

  • 那里有很大的旧代码气味。这不是骗子,因为他正在寻找调用者的 type
  • 这是一个奇怪且非常可疑的要求。您通常希望(并且可以轻松地)限制正在注册的类型。注册商应该不关心谁进行注册。

标签: c# .net methods


【解决方案1】:

这个要求闻起来很糟糕,我怀疑你最好还是换个方式做事。也就是说,以下代码一般可以为您获取调用者的类型。我强烈建议不要使用它,原因有很多(包括它速度慢、容易出错以及我是否提到它很慢?

在使用以下代码之前,请考虑一下您在做什么。

[MethodImpl(MethodImplOptions.NoInlining)]
private static Type GetCallerType()
  int skipFrames = 2; //Assumes that you are calling this from the method being invoked by the caller. Add stack frames to skip for each method removed, and make sure each one is marked with [MethodImpl(MethodImplOptions.NoInlining)]
  Type declaringType;
  do
  {
    MethodBase method = new StackFrame(skipFrames, false).GetMethod();
    declaringType = method.DeclaringType;
    skipFrames++;
  } while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase));
  return declaringType;
}

代码基于NLog's source code

【讨论】:

  • 请您为该要求设计一种不同的方式吗?再次。假设您有一个 AnimalContainer 类(如杂志),然后是一组动物商店。只有 Animal's stores 可以将动物注册到您的“杂志”,然后,任何其他对象(例如一个人、另一家商店、一家餐馆)都可以获取这些动物的实例。你将如何解决这个要求?你会采取什么方法?
  • @mdarefull:让任何调用者注册任何类型,然后将要添加的实际类型约束为预期的超类型。
  • 很抱歉,我认为您不了解问题所在。其实我关心的不是注册的Types,而是可以注册类型的Class。
  • 谁可以注册一个类型有什么关系?
  • 将 AnimalContainer 视为 Animal's Store。您只希望饲养员在商店中出售动物。你不希望任何人出售动物。作为一个更有价值的问题。想象一下,你有一个 IoC 容器。因为您是防御性编码,所以您不希望任何人(除了一组选定的“特殊”类)能够注册容器上的依赖项。如果另一个开发人员追随您,并且不知道“约定”,那么他可能会尝试注册一个实例并可能破坏您的项目。
【解决方案2】:

StackTrace stackTrace = new StackTrace(); Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

【讨论】:

  • 抱歉,这给了我 MethodName,我需要的是调用我的方法的实例的类型。
【解决方案3】:

好吧,您可以使用反射来访问堆栈跟踪 - 但这很慢,在某些情况下,如果 JIT 由于内联而跳过了一些堆栈帧,我不会感到惊讶。

一种选择是使方法内部化,并且仅在同一程序集中包含“允许”的类型。或者将方法设为私有,并且仅将“允许”类型作为嵌套类型包含在外部类型中。

没有比这更细粒度的了 - 如果您需要的不仅仅是程序集中的“允许”类型,您应该考虑您的信任模型...您对其他代码有多少控制权最终会出现在您的组装中吗?如果您不信任的其他人可以更改该代码,那么游戏就结束了,他们可以撤消您添加的任何保护。

如果您试图防止意外做错事,您可以使用 Roslyn 代码诊断,可能...但从根本上说,这种事情只能带您到此为止。

【讨论】:

  • @JonSkeet 先生:AnimalContainer 是引用图中的接收器节点。在该节点上包含“动物的发布者”会破坏以循环引用结尾的属性。
  • @mdarefull:对不起,我根本不遵守。引用是指汇编引用还是对象引用?
  • 程序集参考,当然。 AnimalContainer 应该可以被所有其他对象访问,并且 Factory Classes(允许的类型集)应该具有对所有对象的引用。将两者放在同一个程序集中会创建一个循环引用。无论如何,当你假设我想要达到的目标时,你是对的。我正在尝试进行防御性编码。当然,我可以创建一个关于哪些类型将实例注册到容器的“约定”,但是如果不了解该约定的新开发人员尝试在不允许的地方和时刻发布实例会发生什么跨度>
  • @mdarefull:如果您不能相信新开发人员会阅读您的文档或与团队其他成员交谈(假设这是您的内部代码),那么就会出现更大的问题。
  • 或者根本不是由解决方案的原始设计构思的。这会破坏某些东西,并且很难找到原因。通过对允许的类型进行代码限制,我避免了这种情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多