【问题标题】:Force base method call强制基方法调用
【发布时间】:2011-04-28 07:34:00
【问题描述】:

Java 或 C# 中是否存在强制继承类调用基本实现的构造?您可以调用 super() 或 base() 但如果不调用它是否有可能引发编译时错误?那会很方便..

--编辑--

我主要对重写方法感到好奇。

【问题讨论】:

  • 你的意思是从构造函数的时候重写一个方法吗?
  • 我希望您为您的问题选择了一种语言 - Java 或 C#。只是从使问题对后来的人更有用的角度来看。也许 Java 的答案与 C# 的答案不同。

标签: c# java inheritance polymorphism super


【解决方案1】:

没有也不应该这样做。

如果在基类中有这样的东西,我能想到的最接近的东西:

public virtual void BeforeFoo(){}

public void Foo()
{

this.BeforeFoo();
//do some stuff
this.AfterFoo();

}

public virtual void AfterFoo(){}

并允许继承类覆盖 BeforeFoo 和/或 AfterFoo

【讨论】:

  • 您的建议似乎是该问题的常见解决方案。尽管让它强制(或自动调用它)继承类调用它会很方便,并且会使某些错误很容易找到。您能否详细说明为什么不应该有任何功能可以做到这一点?它是否违反了基本的多态性概念,如果是,为什么?
  • 如果我有一个类扩展了另一个类,这迫使我在我的 Foo 中的某处调用 base.Foo(),这是相当有限的。如果我想要那个功能,我要么调用 base.Foo(),要么我不能使用这个类来扩展我的。编译器应该强制执行一组最小的基本规则。您应该查看 .NET msdn.microsoft.com/en-us/devlabs/dd491992.aspx 的代码合同
  • 我看不出它有什么限制,如果你有一个必须调用基方法的类,那是设计选择,如果基方法调用是可选的,那么你不要使用假设的关键字强制碱基调用。在必须始终调用基本方法的情况下,我看不出它的限制..因为设计者特别选择了这种方式。
  • "而且不应该这样做。"当然应该有,就像抽象类强制覆盖或接口强制方法实现一样。这是一个普遍的需求,如果实施就不会搅浑水。
【解决方案2】:

Java 中没有。这在 C# 中可能是可能的,但其他人必须对此进行说明。

如果我理解正确你想要这个:

class A {
    public void foo() {
        // Do superclass stuff
    }
}

class B extends A {
    public void foo() {
        super.foo();
        // Do subclass stuff
    }
}

您可以在 Java 中执行以下操作来强制使用超类 foo:

class A {
    public final void foo() {
        // Do stuff
        ...
        // Then delegate to subclass
        fooImpl();
    }

    protected abstract void fooImpl();
}

class B extends A {
    protected void fooImpl() {
        // Do subclass stuff
    }
}

它很丑,但它实现了你想要的。否则,您只需要小心确保调用超类方法即可。

也许您可以修改您的设计来解决问题,而不是使用技术解决方案。这可能是不可能的,但可能值得考虑。

编辑:也许我误解了这个问题。您是在谈论一般的构造函数或方法吗?我一般假设方法。

【讨论】:

  • +1:我不认为它难看。这是模板方法模式,也是正确的方法。 fooImpl() 这个名字很难看,但这只是一个例子......
【解决方案3】:

以下示例在覆盖方法时未继承基本功能时抛出InvalidOperationException

这对于某些内部 API 调用方法的场景可能很有用。

即其中Foo() 不是为直接调用而设计的:

public abstract class ExampleBase {
    private bool _baseInvoked;

    internal protected virtual void Foo() {
        _baseInvoked = true;
        // IMPORTANT: This must always be executed!
    }

    internal void InvokeFoo() {
        Foo();
        if (!_baseInvoked)
            throw new InvalidOperationException("Custom classes must invoke `base.Foo()` when method is overridden.");
    }
}

作品:

public class ExampleA : ExampleBase {
    protected override void Foo() {
        base.Foo();
    }
}

大喊:

public class ExampleB : ExampleBase {
    protected override void Foo() {
    }
}

【讨论】:

    【解决方案4】:

    你可能想看看这个(调用超级反模式)http://en.wikipedia.org/wiki/Call_super

    【讨论】:

      【解决方案5】:

      如果我理解正确你想强制你的基类行为不被覆盖,但仍然能够扩展它,那么我会使用模板方法设计模式,并且在 C# 中不要在方法定义。

      【讨论】:

        【解决方案6】:

        我使用以下技术。注意Hello()方法是受保护的,所以不能从外部调用...

        public abstract class Animal
        {
            protected abstract void Hello();
        
            public void SayHello()
            {
                //Do some mandatory thing
                Console.WriteLine("something mandatory");
        
                Hello();
        
                Console.WriteLine();
            }
        }
        
        public class Dog : Animal
        {
            protected override void Hello()
            {
                Console.WriteLine("woof");
            }
        }
        
        public class Cat : Animal
        {
            protected override void Hello()
            {
                Console.WriteLine("meow");
            }
        }
        

        示例用法:

        static void Main(string[] args)
        {
            var animals = new List<Animal>()
            {
                new Cat(),
                new Dog(),
                new Dog(),
                new Dog()
            };
        
            animals.ForEach(animal => animal.SayHello());
            Console.ReadKey();
        }
        

        产生:

        【讨论】:

          【解决方案7】:

          没有。这不可能。如果你必须有一个函数来执行一些 pre 或 post 操作,请执行以下操作:

          internal class Class1
          {
             internal virtual void SomeFunc()
             {
                // no guarantee this code will run
             }
          
          
             internal void MakeSureICanDoSomething()
             {
                // do pre stuff I have to do
          
                ThisCodeMayNotRun();
          
                // do post stuff I have to do
             }
          
             internal virtual void ThisCodeMayNotRun()
             {
                // this code may or may not run depending on 
                // the derived class
             }
          }
          

          【讨论】:

            【解决方案8】:

            我没有阅读这里的所有回复;但是,我正在考虑同样的问题。在回顾了我真正想做的事情之后,在我看来,如果我想强制调用基本方法,我不应该首先声明基本方法虚拟(可覆盖)。

            【讨论】:

              【解决方案9】:

              不要强制碱基调用。让父方法做你想做的事,同时在其主体中调用可覆盖(例如:抽象)受保护的方法。

              【讨论】:

                【解决方案10】:

                不要认为内置任何可行的解决方案。不过,我确信有单独的代码分析工具可以做到这一点。

                【讨论】:

                  【解决方案11】:

                  EDIT 将构造误读为构造函数。保留为 CW,因为它适合问题的一个非常有限的子集。

                  在 C# 中,您可以通过定义一个在基类型中至少具有一个参数的构造函数来强制执行此行为。这将删除默认构造函数并强制派生类型显式调用指定的基类,否则会出现编译错误。

                  class Parent {
                    protected Parent(int id) {
                    }
                  }
                  
                  class Child : Parent { 
                    // Does not compile
                    public Child() {}
                    // Also does not compile
                    public Child(int id) { }
                    // Compiles
                    public Child() :base(42) {}
                  
                  }
                  

                  【讨论】:

                  • 我认为 OP 是在谈论能够强制 public override void Method() {base.Method(); ...},而不是关于构造函数。
                  • @Ani,@Matti,是的,我将构造误读为构造函数:)。更新了答案。
                  【解决方案12】:

                  在 java 中,编译器只能在构造函数的情况下强制执行此操作。

                  必须在继承链的整个过程中调用构造函数。即,如果 Dog 扩展 Animal 扩展 Thing,则 Dog 的构造函数必须调用 Animal 的构造函数,必须调用 Thing 的构造函数。

                  常规方法并非如此,程序员必须在必要时显式调用超级实现。

                  强制运行某些基本实现代码的唯一方法是将可覆盖的代码拆分为单独的方法调用:

                  public class Super
                  {
                      public final void doIt()
                      {
                      // cannot be overridden
                          doItSub();
                      }
                  
                      protected void doItSub()
                      {
                       // override this
                      }
                  }
                  
                  public class Sub extends Super
                  {
                      protected void doItSub()
                      {
                       // override logic
                      }
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    我偶然发现了这篇文章,并不一定喜欢任何特定的答案,所以我想我会提供自己的...

                    C# 中无法强制调用基方法。因此,这样的编码被认为是一种反模式,因为后续开发人员可能没有意识到他们必须调用基方法,否则该类将处于不完整或错误状态。

                    但是,我发现需要这种类型的功能并且可以相应地实现的情况。通常派生类需要基类的资源。为了获取通常可能通过属性公开的资源,它改为通过方法公开。派生类别无选择,只能调用方法获取资源,从而保证基类方法被执行。

                    人们可能会问的下一个合乎逻辑的问题是为什么不把它放在构造函数中呢?原因是它可能是操作顺序问题。在构造类时,可能仍然缺少一些输入。

                    这是否避开了问题?是和不是。是的,它确实强制派生类调用特定的基类方法。不,它不会使用 override 关键字来执行此操作。这可能对寻找这篇文章答案的个人有所帮助吗?

                    我没有把它当作福音来宣传,如果有人看到这种方法的缺点,我很想听听。

                    【讨论】:

                      【解决方案14】:

                      在 Android 平台上有一个名为“CallSuper”的 Java 注释,它强制在编译时调用基本方法(尽管此检查非常基础)。可能相同类型的机制可以很容易地以相同的方式在 Java 中实现。 https://developer.android.com/reference/androidx/annotation/CallSuper

                      【讨论】:

                        猜你喜欢
                        • 2010-10-21
                        • 2018-10-15
                        • 2019-05-24
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2020-12-03
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多