在 C# 中确定调用对象类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/658253/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 12:10:33  来源:igfitidea点击:

Determine Calling Object Type in C#

c#types

提问by DevinB

Regardless of whether or not this is a good idea, is it possible to implement an interface where the executing function is aware of the calling object's type?

不管这是否是一个好主意,是否有可能实现一个接口,其中执行函数知道调用对象的类型?

class A
{
   private C;

   public int doC(int input)
   {
      return C.DoSomething(input);
   }
}

class B
{
   private C;

   public int doC(int input)
   {
      return C.DoSomething(input);
   }
}

class C
{
   public int DoSomething(int input)
   {
      if(GetType(CallingObject) == A)
      {
         return input + 1;
      }
      else if(GetType(CallingObject) == B)
      {
         return input + 2;
      } 
      else
      {
         return input + 3;
      }
   }
}

It seems to me like this is a bad coding practice (because the parameters don't change, but the output would) but aside from that is it possible?

在我看来,这是一种糟糕的编码实践(因为参数不会改变,但输出会改变)但除此之外还有可能吗?

I'm in a situation were I want a few specific types to be able to call a certain function, but I can't exclude access to the function. I thought about having a "type" parameter

我处于一种情况,我希望一些特定类型能够调用某个函数,但我不能排除对该函数的访问。我想过有一个“类型”参数

DoSomething(int input, Type callingobject)

But there's no guarantee that the calling object would use GetType(this), as opposed to GetType(B) to spoof a B regardless of their own type.

但是不能保证调用对象会使用 GetType(this),而不是 GetType(B) 来欺骗 B,而不管它们自己的类型如何。

Would this be as simple (relatively simple) as examining the callstack?

这会像检查调用堆栈一样简单(相对简单)吗?

采纳答案by John Feminella

First, yes, it's a terrible idea to do this and breaks all kinds of solid design principles. You should definitely consider an alternative approach if that's open, like simply using polymorphism—this seems like it can be refactored to a pretty clear case of single dispatch.

首先,是的,这样做是一个糟糕的主意,并且打破了各种可靠的设计原则。如果它是开放的,你绝对应该考虑另一种方法,比如简单地使用多态——这似乎可以重构为一个非常清晰的单分派情况。

Secondly, yes, it's possible. Use System.Diagnostics.StackTraceto walk the stack; then get the appropriate StackFrameone level up. Then determine which method was the caller by using GetMethod()on that StackFrame. Note that building a stack trace is a potentially expensive operation, and it's possible for callers of your method to obscure where things are really coming from.

其次,是的,这是可能的。使用System.Diagnostics.StackTrace走栈; 然后得到适当的StackFrame一级。然后通过GetMethod()在 that 上使用来确定哪个方法是调用者StackFrame。请注意,构建堆栈跟踪是一项潜在的昂贵操作,并且您的方法的调用者可能会模糊事物的真正来源。



Edit:This comment from the OP makes it pretty clear that this could probably be a generic or polymorphic method. @devinb, you might want to consider making a new question that provides more detail about what you're trying to do, and we can see if it lends itself well to a good solution.

编辑:来自 OP 的这条评论清楚地表明这可能是一个通用或多态方法。@devinb,您可能需要考虑提出一个新问题,提供有关您要执行的操作的更多详细信息,我们可以看看它是否适合一个好的解决方案。

The short version is that I would end up have 30 or 40 identical functions that were simply off by one or two lines. – devinb (12 secs ago)

简而言之,我最终会拥有 30 或 40 个相同的功能,而这些功能仅相差一两行。– devinb(12 秒前)

回答by Rex M

There is (almost) always a proper design that can accomplish what you need. If you take one step back to describe what you actually need to do, I am confident you'll get at least one good design that doesn't require you to have to resort to something like this.

(几乎)总有一个合适的设计可以满足您的需求。如果您退后一步来描述您实际需要做的事情,我相信您至少会得到一种不需要您必须求助于此类事情的优秀设计。

回答by JaredPar

As an alternative approach, have you ever considered offering up a different class based on the type of the object that is asking for the class. Say the following

作为替代方法,您是否考虑过根据请求类的对象类型提供不同的类。说以下

public interface IC {
  int DoSomething();
}

public static CFactory { 
  public IC GetC(Type requestingType) { 
    if ( requestingType == typeof(BadType1) ) { 
      return new CForBadType1();
    } else if ( requestingType == typeof(OtherType) { 
      return new CForOtherType();
    }  
    ...
  }
}

This would be a much cleaner approach than have each method change it's behavior based on the calling object. It would cleanly separate out the concerns to the different implementations of IC. Additionally, they could all proxy back to the real C implementation.

与让每个方法根据调用对象更改其行为相比,这将是一种更简洁的方法。它将清楚地分离出对 IC 不同实现的关注。此外,它们都可以代理回真正的 C 实现。

EDITExamining the callstack

编辑检查调用堆栈

As several other people pointed out you can examine the callstack to determine what object is immediately calling the function. However this is not a foolproof way to determine if one of the objects you want to special case is calling you. For instance I could do the following to call you from SomeBadObject but make it very difficult for you to determine that I did so.

正如其他几个人指出的那样,您可以检查调用堆栈以确定立即调用该函数的对象。但是,这不是确定您想要特殊情况的对象之一是否正在调用您的万无一失的方法。例如,我可以执行以下操作以从 SomeBadObject 调用您,但让您很难确定我这样做了。

public class SomeBadObject {
  public void CallCIndirectly(C obj) { 
    var ret = Helper.CallDoSomething(c);
  }
}

public static class Helper {
  public int CallDoSomething(C obj) {
    return obj.DoSomething();
  }
}

You could of course walk further back on the call stack. But that's even more fragile because it may be a completely legal path for SomeBadObjectto be on the stack when a different object calls DoSomething().

您当然可以在调用堆栈上走得更远。但这更加脆弱,因为SomeBadObject当不同的对象调用DoSomething().

回答by arul

Well you could try grabbing the stack trace and determine the type of the caller from there, which is an overkill in my opinion and would be slow.

好吧,您可以尝试抓取堆栈跟踪并从那里确定调用者的类型,在我看来,这有点矫枉过正,而且速度会很慢。

How about an interface, that A,Bwould implement?

一个接口如何A,B实现?

interface IFoo { 
     int Value { get; } 
}

And then your DoSomethingmethod would look like this:

然后你的DoSomething方法看起来像这样:

   public int DoSomething(int input, IFoo foo)
   {
        return input + foo.Value;
   }

回答by EFrank

You could use the System.Diagnostics.StackTraceclass to create a stack trace. Then you could look for the StackFramethat is associated with the caller. The StackFramehas a Methodproperty that you can use to get to the type of the caller.

您可以使用System.Diagnostics.StackTrace该类来创建堆栈跟踪。然后您可以查找StackFrame与呼叫者关联的 。该StackFrame有一个Method,你可以用它来获得给调用者的类型属性。

However, the above method is nothing that should be used in performance critical code.

但是,上述方法不应该在性能关键代码中使用。

回答by JoshBerke

You could examine the call stack, but that is both expensive and fragile. When your code is jit'ed the compiler might inline your methods so while it could work in debug mode you could get a different stack when compiled in release mode.

您可以检查调用堆栈,但这既昂贵又脆弱。当您的代码被 jit'ed 时,编译器可能会内联您的方法,因此虽然它可以在调试模式下工作,但在发布模式下编译时您可以获得不同的堆栈。

回答by Matt Murrell

The easiest answer would be to pass in the sender object like any event with the typical sender, eventargs methodology.

最简单的答案是像使用典型的发送者 eventargs 方法的任何事件一样传入发送者对象。

Your calling code would look like this:

您的调用代码如下所示:

return c.DoSomething(input, this);

Your DoSomething method would simply check the type using the IS operator:

您的 DoSomething 方法将使用 IS 运算符简单地检查类型:

public static int DoSomething(int input, object source)
{
    if(source is A)
        return input + 1;
    else if(source is B)
        return input + 2;
    else
        throw new ApplicationException();

}

This seems like something with a little more OOP. You might consider C an abstract class with an method, and having A,B inherit from C and simply call the method. This would allow you to check the type of the base object, which is not obviously spoofed.

这似乎是更多 OOP 的东西。您可能认为 C 是一个带有方法的抽象类,并且 A、B 继承自 C 并简单地调用该方法。这将允许您检查基础对象的类型,这不是明显的欺骗。

Out of curiosity, what are you trying with this construct?

出于好奇,你想用这个构造做什么?

回答by dfasdljkhfaskldjhfasklhf

structure it like a event handler, I'm sure FX cop would even suggest you do this

像事件处理程序一样构建它,我相信 FX 警察甚至会建议你这样做

static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            throw new NotImplementedException();
        }

回答by Hasani Blackwell

Not reliably due to the possibility of inlining by the runtime.

由于运行时可能内联,因此不可靠。

回答by Leo

I might be in trouble thinking I would handle this differently but....

我可能会觉得我会以不同的方式处理这个问题,但是......

I assume this:

我假设:

class A calls on class E for information
class C calls on class E for information
both through the same method in E

A 类调用 E 类获取信息
C 类调用 E 类获取信息,
两者都通过 E 中的相同方法

You know and created all three classes and can hide the content of class E to the public. If this were not the case, your controll would always be lost.

您知道并创建了所有三个类,并且可以向公众隐藏类 E 的内容。如果不是这种情况,您将永远失去控制权。

Logically: if you can hide content of class E, you do this most likely by distributing through a DLL.

从逻辑上讲:如果您可以隐藏 E 类的内容,则最有可能通过 DLL 进行分发。

If you do this anyway, why not hide the content of classes A and C as well (same method) but allowing A and C to be used as base for derived classes B and D.

如果您仍然这样做,为什么不也隐藏类 A 和 C 的内容(相同的方法),而是允许将 A 和 C 用作派生类 B 和 D 的基础。

In classes A and C you would have a method to call the method in class E and deliver the result. In that method (usable by the derived class, but the content is hidden to the users) you can have long strings as "keys" passed to the method in class E. These strings could not be guessed by any user and would only be known to the base classes A, C and E.

在 A 类和 C 类中,您将有一个方法来调用 E 类中的方法并传递结果。在该方法中(可由派生类使用,但内容对用户隐藏)您可以将长字符串作为“键”传递给类 E 中的方法。这些字符串不能被任何用户猜到,只能知道到基类 A、C 和 E。

Having the base classes encapsulate the knowledge of how to call the method in E correctly would be perfect OOP implementation I think

我认为让基类封装如何正确调用 E 中方法的知识将是完美的 OOP 实现

Then again... I could be overlooking the complexities here.

再说一遍……我可能会忽略这里的复杂性。