how to improve this method using polymorphism+overloading so as to reduce IS (type check)?

后端 未结 2 1204
栀梦
栀梦 2020-12-10 17:23

For example

BaseClass MyBase()
{
    public int Add(BaseClass next)
    {
        if (this is InheritedA && next is InheritedA)
            return 1;         


        
相关标签:
2条回答
  • 2020-12-10 18:17

    The pattern you are implementing is called double virtual dispatch.

    A single virtual dispatch chooses which method to call on the basis of the run-time type of the receiver and the compile time type of the arguments. This is a traditional virtual dispatch:

    abstract class Animal {}
    class Tiger : Animal {}
    class Giraffe : Animal {} 
    class B
    {
        public virtual void M(Tiger x) {}
        public virtual void M(Animal x) {}
    }
    class D : B
    {
        public override void M(Tiger x) {}
        public override void M(Animal x) {}
    }
    ...
    B b = whatever;
    Animal a = new Tiger();
    b.M(a);
    

    Which method is called? B.M(Tiger) and D.M(Tiger) are not chosen; we reject them based on the compile time type of the argument, which is Animal. But we choose whether to call B.M(Animal) or D.M(Animal) at runtime based on whether whatever is new B() or new D().

    Double virtual dispatch chooses which method to call based on the runtime types of two things. If C# supported double virtual dispatch, which it does not, then the runtime dispatch would go to B.M(Tiger) or D.M(Tiger) even though the compile-time type of the argument is Animal.

    C# 4 does however support dynamic dispatch. If you say

    dynamic b = whatever;
    dynamic a = new Tiger();
    b.M(a);
    

    Then the analysis of M will be done entirely at runtime using the runtime types of b and a. This is significantly slower, but it does work.

    Alternatively, if you want to do double virtual dispatch and get as much analysis done at compile time as possible then the standard way to do that is to implement the Visitor Pattern, which you can look up on the internet easily.

    0 讨论(0)
  • 2020-12-10 18:21

    As was suggested in the comments, if you are able to assign a constant value to each derived, then you can build a much cleaner implementation than I'm describing here by just having a virtual property named Value or similar that is used to for the addition.

    Assuming that isn't an option, you may wish to consider pre-computing the results at the base class level to describe the values that you're assigning for each combination. This can break down and become error prone and tedious as the set of classes grows, so I would suggest only considering this if you expect a very small set to maintain.

    In my rudimentary example, I used a dictionary to hold the set and hard-coded the combinations. From your comment, it appears that none of the basic rules of arithmetic apply, so I've left them out as constraints here. If the result value has no actual meaning and you're just incrementing it, you could consider building the result set using reflection to pull the derived classes and considering each combination.

    public class BaseClass
    {
      private static readonly Dictionary<int, int> addResults = new Dictionary<int, int>();
    
      static BaseClass()
      {
        addResults.Add(CreateKey(typeof(ChildA), typeof(ChildA)), 1);
        addResults.Add(CreateKey(typeof(ChildA), typeof(ChildB)), 2);
        addResults.Add(CreateKey(typeof(ChildB), typeof(ChildA)), 3);
        addResults.Add(CreateKey(typeof(ChildB), typeof(ChildB)), 4);
      }
    
      public static int CreateKey(Type a, Type b)
      {
        return (String.Concat(a.Name, b.Name).GetHashCode());
      }
    
      public int Add(BaseClass next)
      {
        var result = default(int);
    
        if (!addResults.TryGetValue(CreateKey(this.GetType(), next.GetType()), out result))
        {
          throw new ArgumentOutOfRangeException("Unknown operand combination");
        }
    
        return result;
      }
    }
    
    public class ChildA : BaseClass {}
    public class ChildB : BaseClass {}
    
    0 讨论(0)
提交回复
热议问题