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

时间:2009-03-18 13:30:15

标签: c# types

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

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

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

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

DoSomething(int input, Type callingobject)

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

这会像检查callstack一样简单(相对简单)吗?


编辑

请将JaredPar的答案(如果你想的话)以及John Feminella的答案付诸表决。我无法将这两个答案都标记为已被接受,我接受了John Feminella的答案,因为它特别符合我的要求,而Jared的答案则提出了我之前未曾考虑过的解决方案。

11 个答案:

答案 0 :(得分:17)

首先,是的,做到这一点并打破各种坚实的设计原则是一个糟糕的主意。你应该考虑一种替代方法,如果它是开放的,就像简单地使用多态 - 这似乎可以重构为一个非常明确的单一调度的情况。

其次,是的,这是可能的。使用System.Diagnostics.StackTrace走栈;然后获得适当的StackFrame一级。然后使用StackFrame上的GetMethod()确定调用方是哪种方法。请注意,构建堆栈跟踪是一项可能很昂贵的操作,并且您的方法的调用者可能会模糊实际来自哪里。


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

  

简短的版本是我最终会的   有30或40个相同的功能   简单地靠一两行。 - devinb(12秒前)

答案 1 :(得分:13)

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

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

这将是一种更清晰的方法,而不是每种方法都基于调用对象改变它的行为。它将干净地分离出对IC的不同实现的关注。此外,他们都可以代理回到真正的C实现。

编辑检查callstack

正如其他几个人所指出的,你可以检查一下callstack来确定哪个对象立即调用该函数。但是,这不是一种简单的方法来确定您想要特殊情况的对象之一是否正在呼叫您。例如,我可以执行以下操作来从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();
  }
}

你当然可以走回调用堆栈。但是这更加脆弱,因为当另一个对象调用DoSomething时,SomeBadObject可能是一个完全合法的路径。

答案 2 :(得分:4)

从Visual Studio 2012(.NET Framework 4.5)开始,您可以使用调用者属性(VB和C#)自动将caller information传递给方法。

public void TheCaller()
{
    SomeMethod();
}

public void SomeMethod([CallerMemberName] string memberName = "")
{
    Console.WriteLine(memberName); // ==> "TheCaller"
}

调用者属性位于System.Runtime.CompilerServices命名空间中。


这是实施INotifyPropertyChanged

的理想选择
private void OnPropertyChanged([CallerMemberName]string caller = null) {
     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

示例:

private string _name;
public string Name
{
    get { return _name; }
    set
    {
        if (value != _name) {
            _name = value;
            OnPropertyChanged(); // Call without the optional parameter!
        }
    }
}

答案 3 :(得分:2)

最简单的答案是传递发件人对象,就像使用典型发件人,eventargs方法的任何事件一样。

您的主叫代码如下所示:

return c.DoSomething(input, this);

您的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();

}

这似乎是一些OOP更多的东西。您可以将C视为具有方法的抽象类,并使A,B继承自C并简单地调用该方法。这将允许您检查基础对象的类型,这显然不会被欺骗。

出于好奇,你在尝试用这个结构做什么?

答案 4 :(得分:2)

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

答案 5 :(得分:0)

(几乎)总有一个合适的设计可以满足您的需求。如果你退后一步来描述你真正需要做的事情,我相信你至少会得到一个好的设计,不要求你不得不诉诸这样的事情。

答案 6 :(得分:0)

那么你可以尝试抓住堆栈跟踪并从那里确定调用者的类型,这在我看来是一种矫枉过正,并且会很慢。

A,B将实现的界面怎么样?

interface IFoo { 
     int Value { get; } 
}

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

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

答案 7 :(得分:0)

您可以使用System.Diagnostics.StackTrace类来创建堆栈跟踪。然后,您可以查找与调用者关联的StackFrameStackFrame具有Method属性,您可以使用该属性来获取调用者的类型。

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

将其结构化为事件处理程序,我确信FX警察甚至建议你这样做

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

答案 10 :(得分:0)

我可能会遇到麻烦,以为我会以不同的方式处理这个......但

我假设:

A类呼叫E级以获取信息 C级呼叫E级以获取信息 两者都是通过E

中的相同方法

您知道并创建了所有三个类,并且可以向公众隐藏E类的内容。如果情况并非如此,那么您的控制权将永远丢失。

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

如果你这样做,为什么不隐藏A类和C类的内容(相同的方法),但允许A和C作为派生类B和D的基础。

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

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

然后......我可以忽略这里的复杂情况。