Search code examples
c#oopsolid-principlesdesign-principles

liskov substitution principle and exception handling


It says that Derived class should not throw any exception which is not known to the Base class, i am trying to find how its work, in the base class i am throwing System.Exception and in Derived i am throwing ArgNullException(). Can someone explain is this is fine

 class b
        {
           virtual public void foo()
           {
               try
               {
                   if (true)
                       throw  new System.Exception();
               }
               catch (Exception ex)
               {
                   Console.WriteLine("in base");

               }
           }


        }
        class a : b
        {   
            override public void foo() 
            {
                try
                {
                    if (true)
                        throw new ArgumentNullException();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("in dervied");
                }
            }           

        }

Solution

  • class MyClass
    {
        public virtual void Foo()
        {
            if (true)
                 throw new System.Exception();
            }
        }
    }
    
    class MyDerivedClass : MyClass
    {   
        public override void Foo() 
        {
            if (true)
                throw new ArgumentNullException();
            }
        }           
    }
    
    
    public class Program
    {
        public static void Main()
        {
            try
            {
                // a factory creating the correct 
                // MyClass derived instance
                var myClass = someFactory.Create();
    
                myClass.Foo();
            }
            catch (Exception)
            {
                // will work.
            }
        }
    }
    

    In this case you are throwing the least specific exception possible in the base class (why that sucks is another discussion). As in such, anyone using the sub classes will be able to catch that, no matter how specific exception you throw.


    Let's say that it had been the other way around. The base class throws ArgumentNullException and the sub class Exception. Now, anyone only knowing about the base class would only have catch blocks for ArgumentNullException as that is what they expect. Their application will therefore fail when the sub class throws Exception.

    class MyClass
    {
        public virtual void Foo()
        {
            if (true)
                 throw new ArgumentNullException();
            }
        }
    }
    
    class MyDerivedClass : MyClass
    {   
        public override void Foo() 
        {
            if (true)
                throw new Exception();
            }
        }           
    }
    
    
    public class Program
    {
        public static void Main()
        {
            try
            {
                // a factory creating the correct 
                // MyClass derived instance
                var myClass = someFactory.Create();
    
                myClass.Foo();
            }
            catch (ArgumentNullException)
            {
                // won't work since the subclass 
                // violates LSP
            }
        }
    }