简体   繁体   中英

c# inheritance multiple parents

   namespace NUnitTestAbstract
{
    public abstract class BaseTestClass1
    {
        public abstract void BaseTestClass1Method();
    }

    public abstract class BaseTestClass2 : BaseTestClass1
    {
        // not overriding BaseTestClass1Method, child classes should do
        public void SomeTestMethodA() { }

        public void SomeTestMethodB() { }
    }

    public abstract class BaseTestClass3 : BaseTestClass1
    {
        // not overriding BaseTestClass1Method, child classes should do
        public void SomeTestMethodX() { }

        public void SomeTestMethodY() { }
    }

    public class TestClass2A : BaseTestClass2
    {
        public override void BaseTestClass1Method()
        {
            // do stuff
        }
    }

    public class TestClass2B : BaseTestClass2
    {
        public override void BaseTestClass1Method()
        {
            // do same stuff as TestClassA
        }
    }


    public class TestClass3A : BaseTestClass3
    {
        public override void BaseTestClass1Method()
        {
            // do stuff
        }
    }

    public class TestClass3B : BaseTestClass3
    {
        public override void BaseTestClass1Method()
        {
            // do same stuff as TestClass3A
        }
    }
}

At this moment I have the above construction. A base class extending another base class. The base class is extended by two child classes. The implementation of BaseTestClass1Method is the same for the two child classes. If it was possible to extends from two parents I would create a class extending BaseTestClass1 implementing BaseTestClass1Method . The two child classes would then extend BaseTestClass2 and the new class so that I only have to implement BaseTestClass1Method once When I add another BaseTestClass3 (same level as BaseTestClass2) extending from BaseTestClass1 and create child classes from it, I have more duplicate code for the implementation of BasetestClass1Method. How can I solve this with the correct pattern?

Here's a more implemented example:

namespace NUnitTestAbstract
{
    public interface IServer { }

    public abstract class ServerBase
    {
        public abstract IServer GetServerInstance();
    }

    public abstract class SomeTests1Base : ServerBase
    {
        // not overriding BaseTestClass1Method, child classes should do
        public void SomeTestMethodA() { }

        public void SomeTestMethodB() { }
    }

    public abstract class SomeTests2Base : ServerBase
    {
        // not overriding BaseTestClass1Method, child classes should do
        public void SomeTestMethodX() { }

        public void SomeTestMethodY() { }
    }

    public class TestClass2A : SomeTests1Base
    {
        public override IServer GetServerInstance()
        {
            IServer serverX = null;
            return serverX;
        }
    }

    public class TestClass2B : SomeTests1Base
    {
        public override IServer GetServerInstance()
        {
            IServer serverX = null;
            return serverX;
        }
    }


    public class TestClass3A : SomeTests2Base
    {
        public override IServer GetServerInstance()
        {
            IServer serverY = null;
            return serverY;
        }
    }

    public class TestClass3B : SomeTests2Base
    {
        public override IServer GetServerInstance()
        {
            IServer serverY = null;
            return serverY;
        }
    }
}

deriving from two classes (or abstract classes) is not supported due to the Diamond problem . An instance of type D when calling a parent method - how does it know of which parent?

在此输入图像描述

Instead of deep inheritance trees with many abstract classes use interfaces and composition.

  • Interfaces to define the API
  • Composition to pass specific implementation.

Have your logic be defined with some interface and have the different concrete types for that specific logic. Then a class like TestClassb can create an instance of the desired logic or better still depend on it in its constructor:

public class Derived 
{
    public Derived(ILogic someDependentLogic) { }
}

In your code comments you say about TestClass2B that it has same behavior as TestClassA but it instead derives from BaseTestClass2 . You should properly think about your inheritance tree. Should TestClass2B be a type of BaseTestClass2 ? Or should it be a type of TestClassA ? If it is actually a "kind of" TestClassA then inherit it instead.

All together the answers are domain specific but I recommend you read about composition: It will help you understand what kind of relationships you have and what is the correct design for your situation.

  1. Composition vs. Inheritance
  2. Prefer composition over inheritance?
  3. Difference between Inheritance and Composition

Further there are some good Design Patterns to look at, such as State , Strategry , Decorator under dofactory

After seeing the advantages of Composition and when to use it you can have a look at SOLID and Inversion Of Control it will help it all fit nicely together.

Use interfaces instead of abstract classes. You can inherit from multiple interfaces, but only from one class.

I didn't understand why are you just not overriding BaseTestClass1Method in BaseTestClass2

    public abstract class BaseTestClass2 : BaseTestClass1
    {
        public override void BaseTestClass1Method()
        {
            // do same stuff as TestClassA
        }
    }

To achieve such goal you might use different patterns like

Strategy Pattern : http://www.dofactory.com/net/strategy-design-pattern

State Pattern : http://www.dofactory.com/net/state-design-pattern

you could implement BaseTestClass1Method in BaseTestClass2 so that both TestClassA and TestClassB get the same implementation. Depending on your requirements however, going this path might end in inheritance hell.

Instead of inheriting BaseTestClass2 from BaseTestClass1 you can pass an instance that implements BaseTestClass1 into BaseTestClass2 via its constructor (dependence injection (DI)). Then in TestClassA and TestClassB simply call the BaseTestClass1Method method of the injected in instance. This reduces your inheritance levels to just 1.

Use a DI container such as Autofac or Unity to inject the correct implentation of BaseTestClass1 into decendants of BaseTestClass2.

UPDATE

So working from your example

namespace NUnitTestAbstract
{
    public interface IServer { }

    public class BaseServer()
    {
        private IServer _server;

        public BaseServer(IServer server)
        {
            _server = server;
        }

        public IServer GetServerInstance()
        {
            return _server;
        }
    }

    public class SomeTests1 : ServerBase
    {
        // not overriding BaseTestClass1Method, child classes should do
        public void SomeTestMethodA() { }

        public void SomeTestMethodB() { }
    }

    public class SomeTests2 : ServerBase
    {
        // not overriding BaseTestClass1Method, child classes should do
        public void SomeTestMethodX() { }

        public void SomeTestMethodY() { }
    }
}

public class ServerA : IServer
{
    //Implementation
}

public class ServerB : IServer
{
    //Implementation
}

var someTests1 = new SomeTests1(new ServerA()); var someTests2 = new SomeTests2(new ServerB());

Note that I've removed the abstract classes and injected the server implementations into the new instances.

I've dropped the TestClass(1..n) because I cannot see a need for them but only you can know the answer to that.

Also I've written this code on the fly so it has not been tested nor have I checked if it even compiles but you should be able to get the gist of it. Hope it helps.

Use the interface as suggested by others:

 namespace NUnitTestAbstract
    {
        public interface IBaseTes1
        {
            void BaseTestClass1Method();
        }

        public abstract class BaseTestClass2 : IBaseTes1
        {
            void IBaseTes1.BaseTestClass1Method()
            {

            }

            // not overriding BaseTestClass1Method, child classes should do
            public void SomeTestMethodA() { }

            public void SomeTestMethodB() { }
        }

        public class TestClassA : BaseTestClass2
        {
        }

        public class TestClassb : BaseTestClass2
        {
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM