代码之家  ›  专栏  ›  技术社区  ›  DevinB

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

  •  14
  • DevinB  · 技术社区  · 15 年前

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

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

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

    我想用一个“type”参数

    DoSomething(int input, Type callingobject)
    

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

    这是否与检查调用堆栈一样简单(相对简单)?

    11 回复  |  直到 4 年前
        1
  •  18
  •   Ðаn    4 年前

    首先,是的,这是一个可怕的想法,打破了各种坚实的设计原则。如果这是公开的,你应该明确地考虑另一种方法,就像简单地使用多态性一样,这似乎可以重构为一个非常清楚的单一调度的情况。

    其次,是的,这是可能的。使用 System.Diagnostics.StackTrace 在堆栈中行走;然后得到合适的答案 StackFrame 再上一层。然后通过使用确定调用方是哪个方法 GetMethod() 在那上面 调用栈 . 请注意,构建堆栈跟踪是一项潜在的昂贵操作,方法的调用者可能会模糊事情的真正来源。


    编辑: OP的这一评论清楚地表明,这可能是一种通用的或多态的方法。 @德文布 你可能想考虑一个新的问题,它提供了关于你正在做什么的更多细节,我们可以看看它是否很好地解决了问题。

    简短的说法是我最终会 具有30或40个相同的功能 只需要一两行就行了。devinb(12秒前)

        2
  •  14
  •   Ðаn    4 年前

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

    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实现。

    编辑 检查调用堆栈

    正如其他一些人指出的那样,您可以检查调用堆栈以确定哪个对象立即调用函数。然而,这并不是一种简单易行的方法来确定您想要特殊情况下的某个对象是否正在调用您。例如,我可以做下面的操作,从某个badObject给你打电话,但是让你很难确定我是这样做的。

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

    SomeBadObject 当另一个对象调用时位于堆栈上 DoSomething() .

        3
  •  4
  •   Olivier Jacot-Descombes    11 年前

    从Visual Studio 2012(.NET Framework 4.5)开始,您可以自动通过 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!
            }
        }
    }
    
        4
  •  2
  •   Matt Murrell    15 年前

    您的呼叫代码如下所示:

    return c.DoSomething(input, this);
    

    您的剂量测量方法只需使用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();
    
    }
    

    这看起来更像是一个面向对象的东西。您可以将C类抽象为具有方法的类,并且具有A、B从C继承并简单调用该方法的方式。这将允许您检查基本对象的类型,这显然不是伪造的。

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

        5
  •  2
  •   Hasani Blackwell    15 年前

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

        6
  •  0
  •   Rex M    15 年前

    (几乎)总有一个合适的设计可以满足你的需要。如果你退一步来描述你真正需要做什么,我相信你至少会得到一个好的设计,而不需要你求助于这样的东西。

        7
  •  0
  •   arul    15 年前

    您可以尝试获取堆栈跟踪并从中确定调用方的类型,在我看来,这是一种过分的做法,而且速度会很慢。

    那界面呢 A,B

    interface IFoo { 
         int Value { get; } 
    }
    

    然后你的 DoSomething 方法如下所示:

       public int DoSomething(int input, IFoo foo)
       {
            return input + foo.Value;
       }
    
        8
  •  0
  •   JoshBerke    15 年前

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

        9
  •  0
  •   dfasdljkhfaskldjhfasklhf    15 年前

    static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
            {
                throw new NotImplementedException();
            }
    
        10
  •  0
  •   random_user_name Falguni Panchal    12 年前

    我可能在想我会以不同的方式处理这件事时遇到麻烦,但是。。。。

    我假设:

    A类呼叫E类获取信息
    C类呼叫E类获取信息
    两者都通过相同的方法在E

    您知道并创建了这三个类,并且可以向公众隐藏E类的内容。如果不是这样的话,你的控制权就会永远失去。

    逻辑上:如果可以隐藏类E的内容,那么很可能通过DLL分发。

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

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

    然后再一次。。。我可以忽略这里的复杂性。

        11
  •  0
  •   Vahid Farahmandian    5 年前

    你可以使用 System.Diagnostics.StackTrace 类创建堆栈跟踪。然后你就可以找 StackFrame 与调用方关联的。 这个 调用栈 有一个 Method 属性,可以使用该属性获取调用方的类型。

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